home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / gui.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  99.9 KB  |  2,601 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. from __future__ import division
  5. import os
  6. import re
  7. import sys
  8. import time
  9. import socket
  10. import cPickle
  11. import textwrap
  12. import threading
  13. import webbrowser
  14. from win32com.shell import shell, shellcon
  15. import wx
  16. import wx.lib.newevent as wx
  17. from wxp import *
  18. from spamexperts import license
  19. from spamexperts import Version
  20. from spamexperts import dnslookup
  21. from spamexperts import software_update
  22. from spamexperts.message import SEHeaderMessage
  23. from spamexperts.Options import options, optionsPathname
  24. from spamexperts.OptionsClass import IS_HAM, IS_SPAM, IS_UNSURE
  25. from model import VIEW_SPAM, VIEW_HAM, VIEW_BLACK, VIEW_WHITE, VIEW_UNSURE
  26. from spamexperts.resources import get_image_filename, application_directory
  27. (ExitModel, EVT_EXIT_MODEL) = wx.lib.newevent.NewEvent()
  28. (DoUpdate, EVT_DO_UPDATE) = wx.lib.newevent.NewEvent()
  29. (ConcludeUpdate, EVT_CONCLUDE_UPDATE) = wx.lib.newevent.NewEvent()
  30. (SubmitReport, EVT_SUBMIT_REPORT) = wx.lib.newevent.NewEvent()
  31. (WelcomeEvent, EVT_DO_WELCOME) = wx.lib.newevent.NewEvent()
  32. (ProButtonEvent, EVT_PRO_BUTTON) = wx.lib.newevent.NewEvent()
  33. TYPE_EMAIL_MSGS = 0
  34. TYPE_EMAIL_ADDRESS = 1
  35.  
  36. class ButtonDropTarget(wx.PyDropTarget):
  37.     
  38.     def __init__(self, handler, model, acceptedData):
  39.         wx.PyDropTarget.__init__(self)
  40.         self.handler = handler
  41.         self.model = model
  42.         self.acceptedData = acceptedData
  43.         self.data = wx.CustomDataObject('SEDrag')
  44.         self.SetDataObject(self.data)
  45.  
  46.     
  47.     def OnEnter(self, x, y, d):
  48.         return d
  49.  
  50.     
  51.     def OnLeave(self):
  52.         pass
  53.  
  54.     
  55.     def OnDrop(self, x, y):
  56.         return True
  57.  
  58.     
  59.     def OnDragOver(self, x, y, d):
  60.         if hasattr(self.model, 'drag') and self.model.drag in self.acceptedData:
  61.             return wx.DragMove
  62.         
  63.         return wx.DragNone
  64.  
  65.     
  66.     def OnData(self, x, y, d):
  67.         if self.GetData():
  68.             cargo = self.data.GetData()
  69.             list = cPickle.loads(cargo)
  70.             self.handler(list)
  71.         
  72.         return d
  73.  
  74.  
  75.  
  76. class ListDropTarget(wx.PyDropTarget):
  77.     
  78.     def __init__(self, handler):
  79.         wx.PyDropTarget.__init__(self)
  80.         self.handler = handler
  81.         self.data = wx.CustomDataObject('SEListDrag')
  82.         self.SetDataObject(self.data)
  83.         self.match = False
  84.  
  85.     
  86.     def OnEnter(self, x, y, d):
  87.         return d
  88.  
  89.     
  90.     def OnLeave(self):
  91.         pass
  92.  
  93.     
  94.     def OnDrop(self, x, y):
  95.         return True
  96.  
  97.     
  98.     def OnDragOver(self, x, y, d):
  99.         if self.match:
  100.             return wx.DragMove
  101.         
  102.         return wx.DragNone
  103.  
  104.     
  105.     def OnData(self, x, y, d):
  106.         if self.GetData():
  107.             cargo_pickle = self.data.GetData()
  108.             cargo = cPickle.loads(cargo_pickle)
  109.             self.handler(cargo)
  110.         
  111.         return d
  112.  
  113.  
  114.  
  115. class ButtonBar(BoxPanel):
  116.     
  117.     def __init__(self, parent, model):
  118.         BoxPanel.__init__(self, parent)
  119.         self.SetBackgroundColour((225, 225, 225))
  120.         self.spam = ImgButton(self, '', wx.Image(get_image_filename('spam-normal')))
  121.         self.unsure = ImgButton(self, '', wx.Image(get_image_filename('unsure-normal')))
  122.         self.ham = ImgButton(self, '', wx.Image(get_image_filename('ham-normal')))
  123.         self.black = ImgButton(self, '', wx.Image(get_image_filename('blacklist-normal')))
  124.         self.white = ImgButton(self, '', wx.Image(get_image_filename('whitelist-normal')))
  125.         self.UpdateText()
  126.         self.buttons = [
  127.             self.spam,
  128.             self.unsure,
  129.             self.ham,
  130.             self.black,
  131.             self.white]
  132.         self.sizer.AddSpacer((-1, 21))
  133.         self.Add(self.ham, 0, wx.EXPAND | wx.ALL, 5)
  134.         self.Add(self.unsure, 0, wx.EXPAND | wx.ALL, 5)
  135.         self.Add(self.spam, 0, wx.EXPAND | wx.ALL, 5)
  136.         self.Add(self.black, 0, wx.EXPAND | wx.ALL, 5)
  137.         self.Add(self.white, 0, wx.EXPAND | wx.ALL, 5)
  138.         self.spam.Bind(wx.EVT_BUTTON, self.OnSpam)
  139.         self.unsure.Bind(wx.EVT_BUTTON, self.OnUnsure)
  140.         self.ham.Bind(wx.EVT_BUTTON, self.OnHam)
  141.         self.white.Bind(wx.EVT_BUTTON, self.OnWhite)
  142.         self.black.Bind(wx.EVT_BUTTON, self.OnBlack)
  143.         self.spam.SetDropTarget(ButtonDropTarget(self.OnDropSpam, model, [
  144.             'ham',
  145.             'unsure']))
  146.         self.unsure.SetDropTarget(ButtonDropTarget(self.OnDropUnsure, model, [
  147.             'ham',
  148.             'spam']))
  149.         self.ham.SetDropTarget(ButtonDropTarget(self.OnDropHam, model, [
  150.             'spam',
  151.             'unsure']))
  152.         self.white.SetDropTarget(ButtonDropTarget(self.OnDropWhite, model, [
  153.             'spam',
  154.             'ham',
  155.             'unsure',
  156.             'black']))
  157.         self.black.SetDropTarget(ButtonDropTarget(self.OnDropBlack, model, [
  158.             'spam',
  159.             'ham',
  160.             'unsure',
  161.             'white']))
  162.         model.AddView(self)
  163.         self.model = model
  164.  
  165.     
  166.     def UpdateText(self):
  167.         self.spam.SetTitle(_('Spam'))
  168.         self.unsure.SetTitle(_('Unsure'))
  169.         self.ham.SetTitle(_('Not Spam'))
  170.         self.black.SetTitle(_('Blocked Senders'))
  171.         self.white.SetTitle(_('Allowed Senders'))
  172.         self.spam.SetHelpText(_("Emails on this page have been classified as 'spam'. If a mistake has been made please drag & drop the message to the 'Not Spam' button."))
  173.         self.ham.SetHelpText(_("Emails on this page have been classified as 'not spam'. If a mistake has been made please drag & drop the message to the 'Spam' button."))
  174.         self.unsure.SetHelpText(_("SpamExperts was unable to correctly classify the emails found on this page. Please drag & drop the emails to the appropriate 'Spam'/'Not Spam' button."))
  175.         self.white.SetHelpText(_("Emails sent from addresses on this page will always be considered 'not spam' and will bypass all filtering technologies. The content of the message will not be trained and therefore it will not improve the smartness of SpamExperts."))
  176.         self.black.SetHelpText(_("Emails sent from addresses on this page will always be considered 'spam' and will bypass all filtering technologies. The content of the message will not be trained and therefore it will not improve the smartness of SpamExperts."))
  177.         self.Layout()
  178.  
  179.     
  180.     def UpdateView(self, model):
  181.         map((lambda cntr: cntr.SetToggle(False)), self.buttons)
  182.         self.buttons[model.active].SetToggle(True)
  183.  
  184.     
  185.     def OnDropSpam(self, drop_contents):
  186.         pass
  187.  
  188.     
  189.     def OnDropHam(self, drop_contents):
  190.         pass
  191.  
  192.     
  193.     def OnDropUnsure(self, drop_contents):
  194.         pass
  195.  
  196.     
  197.     def OnDropWhite(self, drop_contents):
  198.         pass
  199.  
  200.     
  201.     def OnDropBlack(self, drop_contents):
  202.         pass
  203.  
  204.     
  205.     def OnHam(self, evt):
  206.         self.model.UpdateViews(True)
  207.         self.model.SetActive(VIEW_HAM)
  208.  
  209.     
  210.     def OnSpam(self, evt):
  211.         self.model.UpdateViews(True)
  212.         self.model.SetActive(VIEW_SPAM)
  213.  
  214.     
  215.     def OnUnsure(self, evt):
  216.         self.model.UpdateViews(True)
  217.         self.model.SetActive(VIEW_UNSURE)
  218.  
  219.     
  220.     def OnWhite(self, evt):
  221.         self.model.SetActive(VIEW_WHITE)
  222.  
  223.     
  224.     def OnBlack(self, evt):
  225.         self.model.SetActive(VIEW_BLACK)
  226.  
  227.  
  228.  
  229. class EmailView(BoxPanel):
  230.     
  231.     def __init__(self, parent, model):
  232.         BoxPanel.__init__(self, parent)
  233.         self.model = model
  234.         self.subject = wx.StaticText(self, style = wx.ST_NO_AUTORESIZE)
  235.         self.sender = wx.StaticText(self, style = wx.ST_NO_AUTORESIZE)
  236.         self.recipient = wx.StaticText(self, style = wx.ST_NO_AUTORESIZE)
  237.         self.subject_label = wx.StaticText(self)
  238.         self.sender_label = wx.StaticText(self)
  239.         self.recipient_label = wx.StaticText(self)
  240.         self.UpdateText()
  241.         
  242.         try:
  243.             font = wx.Font(-1, wx.DEFAULT, wx.NORMAL, wx.BOLD)
  244.         except wx.PyAssertionError:
  245.             font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.BOLD)
  246.  
  247.         self.subject_label.SetFont(font)
  248.         self.sender_label.SetFont(font)
  249.         self.recipient_label.SetFont(font)
  250.         self.body = wx.TextCtrl(self, style = wx.TE_MULTILINE | wx.TE_RICH2 | wx.TE_READONLY)
  251.         self.subj_sizer = wx.BoxSizer(wx.HORIZONTAL)
  252.         self.subj_sizer.Add(self.subject_label, 0)
  253.         self.subj_sizer.Add(self.subject, 1, wx.EXPAND)
  254.         self.Add(self.subj_sizer, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, 3)
  255.         self.send_sizer = wx.BoxSizer(wx.HORIZONTAL)
  256.         self.send_sizer.Add(self.sender_label, 0)
  257.         self.send_sizer.Add(self.sender, 1, wx.EXPAND)
  258.         self.Add(self.send_sizer, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, 3)
  259.         self.recp_sizer = wx.BoxSizer(wx.HORIZONTAL)
  260.         self.recp_sizer.Add(self.recipient_label, 0)
  261.         self.recp_sizer.Add(self.recipient, 1, wx.EXPAND)
  262.         self.Add(self.recp_sizer, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, 3)
  263.         self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 3)
  264.         self.Add(self.body, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 3)
  265.  
  266.     
  267.     def UpdateText(self):
  268.         self.subject_label.SetTitle(_('Subject:'))
  269.         self.subject_label.SetBestFittingSize()
  270.         self.sender_label.SetTitle(_('From:'))
  271.         self.sender_label.SetBestFittingSize()
  272.         self.recipient_label.SetTitle(_('To:'))
  273.         self.recipient_label.SetBestFittingSize()
  274.  
  275.     whitespace_re = re.compile('\\s+')
  276.     
  277.     def SetHeader(self, element, header):
  278.         header = self.whitespace_re.sub(' ', header)
  279.         element.SetLabel(' ' + header)
  280.         best_width = element.GetBestSize()[0]
  281.         actual_width = element.GetSize()[0]
  282.         if best_width > actual_width:
  283.             new_length = int((actual_width / best_width) * len(header))
  284.             header = header[:new_length - 10] + '...'
  285.             element.SetLabel(' ' + header)
  286.         
  287.  
  288.     
  289.     def SetMsg(self, msg):
  290.         self.SetHeader(self.subject, msg.subject)
  291.         self.SetHeader(self.sender, msg.sender)
  292.         self.SetHeader(self.recipient, msg.recipient)
  293.         self.body.SetValue(msg.body)
  294.  
  295.  
  296.  
  297. class ClassifierList(SortedLC):
  298.     
  299.     def __init__(self, parent, icon):
  300.         SortedLC.__init__(self, parent)
  301.         self.itemDataMap = None
  302.         self.selecting = False
  303.         self.il.Add(icon)
  304.         self.UpdateText()
  305.         self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
  306.         self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp)
  307.         self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
  308.         self.Bind(wx.EVT_CHAR, self.OnChar)
  309.  
  310.     
  311.     def UpdateText(self):
  312.         self.columns = [
  313.             (_('From'), 150),
  314.             (_('Subject'), 220),
  315.             (_('Date'), 100)]
  316.         self.ResetColumns()
  317.  
  318.     
  319.     def DeselectAll(self):
  320.         self.selecting = True
  321.         for i in xrange(self.GetItemCount()):
  322.             self.SetItemState(i, 0, wx.LIST_STATE_SELECTED)
  323.         
  324.         self.selecting = False
  325.  
  326.     
  327.     def SelectAll(self):
  328.         self.selecting = True
  329.         for i in xrange(self.GetItemCount()):
  330.             self.SetItemState(i, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
  331.         
  332.         self.selecting = False
  333.  
  334.     
  335.     def OnChar(self, evt):
  336.         if evt.ControlDown() and evt.GetKeyCode() == 1:
  337.             self.SelectAll()
  338.         elif evt.GetKeyCode() == wx.WXK_DELETE:
  339.             self.GetParent().view.RemoveFromList(None)
  340.         
  341.         evt.Skip()
  342.  
  343.     
  344.     def UpdateData(self, data):
  345.         self.Show(0)
  346.         self.columns = [
  347.             (_('From'), self.GetColumnWidth(0)),
  348.             (_('Subject'), self.GetColumnWidth(1)),
  349.             (_('Date'), self.GetColumnWidth(2))]
  350.         self.itemDataMap = data
  351.         sortTuple = (self._col, self._colSortFlag[self._col])
  352.         focus = self.GetFocusedItem()
  353.         select = self.GetFirstSelected()
  354.         selected = []
  355.         if select != -1:
  356.             while True:
  357.                 select = self.GetNextSelected(select)
  358.                 if select == -1:
  359.                     break
  360.                 
  361.                 selected.append(select)
  362.         
  363.         self.ClearAll()
  364.         self.ResetColumns()
  365.         for key, data in self.itemDataMap.items():
  366.             index = self.InsertImageStringItem(sys.maxint, data[0], 2)
  367.             self.SetStringItem(index, 1, data[1])
  368.             self.SetStringItem(index, 2, time.asctime(time.localtime(data[2])))
  369.             self.SetItemData(index, key)
  370.             if data[4]:
  371.                 self.SetItemTextColour(index, data[4])
  372.                 continue
  373.         
  374.         self.SortListItems(*sortTuple)
  375.         for select in selected:
  376.             self.Select(select)
  377.         
  378.         self.Focus(focus)
  379.         self.Show(1)
  380.  
  381.     
  382.     def ResetColumns(self):
  383.         for column, width in self.columns:
  384.             idx = self.columns.index((column, width))
  385.             self.InsertColumn(idx, column)
  386.             self.SetColumnWidth(idx, width)
  387.         
  388.  
  389.     
  390.     def OnRightDown(self, evt):
  391.         self.pos = evt.GetPosition()
  392.         (item, flags) = self.HitTest(self.pos)
  393.         if flags & wx.LIST_HITTEST_ONITEM:
  394.             if not self.GetItemState(item, wx.LIST_STATE_SELECTED):
  395.                 self.DeselectAll()
  396.                 self.Select(item)
  397.             
  398.         
  399.         evt.Skip()
  400.  
  401.     
  402.     def OnRightUp(self, evt):
  403.         if not hasattr(self, 'pos'):
  404.             self.pos = evt.GetPosition()
  405.         
  406.         (item, flags) = self.HitTest(self.pos)
  407.         if flags & wx.LIST_HITTEST_ONITEM:
  408.             menu = List2Menu(self, self.menuData)
  409.             self.PopupMenu(menu, self.pos)
  410.         
  411.         evt.Skip()
  412.  
  413.     
  414.     def GetSelectedMsgs(self):
  415.         ret = []
  416.         index = self.GetFirstSelected()
  417.         while index != -1:
  418.             ret.append(self.itemDataMap[self.GetItemData(index)])
  419.             index = self.GetNextSelected(index)
  420.         return ret
  421.  
  422.  
  423.  
  424. class MailList(AutoWidthSortedLC):
  425.     
  426.     def __init__(self, parent, icon):
  427.         AutoWidthSortedLC.__init__(self, parent)
  428.         self.itemDataMap = None
  429.         self.il.Add(icon)
  430.         self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
  431.         self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp)
  432.         self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
  433.         self.Bind(wx.EVT_CHAR, self.OnChar)
  434.  
  435.     
  436.     def DeselectAll(self):
  437.         for i in xrange(self.GetItemCount()):
  438.             self.SetItemState(i, 0, wx.LIST_STATE_SELECTED)
  439.         
  440.  
  441.     
  442.     def SelectAll(self):
  443.         for i in xrange(self.GetItemCount()):
  444.             self.SetItemState(i, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
  445.         
  446.  
  447.     
  448.     def OnChar(self, evt):
  449.         if evt.ControlDown() and evt.GetKeyCode() == 1:
  450.             self.SelectAll()
  451.         elif evt.GetKeyCode() == wx.WXK_DELETE:
  452.             self.GetParent().Delete(None)
  453.         elif evt.GetKeyCode() == wx.WXK_DOWN:
  454.             self.MoveSelectionDown(deselect = not evt.ShiftDown())
  455.         elif evt.GetKeyCode() == wx.WXK_UP:
  456.             self.MoveSelectionUp(deselect = not evt.ShiftDown())
  457.         elif evt.GetKeyCode() == wx.WXK_PAGEUP:
  458.             self.ScrollPages(-1)
  459.         elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
  460.             self.ScrollPages(1)
  461.         
  462.  
  463.     
  464.     def UpdateData(self, data):
  465.         if data != self.itemDataMap:
  466.             self.itemDataMap = data
  467.             focus = self.GetFocusedItem()
  468.             select = self.GetFirstSelected()
  469.             selected = []
  470.             if select != -1:
  471.                 while True:
  472.                     select = self.GetNextSelected(select)
  473.                     if select == -1:
  474.                         break
  475.                     
  476.                     selected.append(select)
  477.             
  478.             self.ClearAll()
  479.             self.InsertColumn(0, _('Email'))
  480.             for key, data in self.itemDataMap.items():
  481.                 index = self.InsertImageStringItem(sys.maxint, data, 2)
  482.                 self.SetItemData(index, key)
  483.             
  484.             for select in selected:
  485.                 self.Select(select)
  486.             
  487.             self.Focus(focus)
  488.         
  489.         if self.itemDataMap:
  490.             self.SetColumnWidth(0, wx.LIST_AUTOSIZE)
  491.         else:
  492.             self.SetColumnWidth(0, 40)
  493.  
  494.     
  495.     def UpdateText(self):
  496.         pass
  497.  
  498.     
  499.     def OnRightDown(self, evt):
  500.         self.pos = evt.GetPosition()
  501.         (item, flags) = self.HitTest(self.pos)
  502.         if flags & wx.LIST_HITTEST_ONITEM:
  503.             if not self.GetItemState(item, wx.LIST_STATE_SELECTED):
  504.                 self.DeselectAll()
  505.                 self.Select(item)
  506.             
  507.         
  508.         evt.Skip()
  509.  
  510.     
  511.     def OnRightUp(self, evt):
  512.         if not hasattr(self, 'pos'):
  513.             return None
  514.         
  515.         (item, flags) = self.HitTest(self.pos)
  516.         if flags & wx.LIST_HITTEST_ONITEM:
  517.             menu = List2Menu(self, self.menuData)
  518.             self.PopupMenu(menu, self.pos)
  519.         else:
  520.             menu = List2Menu(self, self.menuData[2:3])
  521.             self.PopupMenu(menu, self.pos)
  522.         evt.Skip()
  523.  
  524.     
  525.     def GetSelectedEmails(self):
  526.         ret = []
  527.         index = self.GetFirstSelected()
  528.         while index != -1:
  529.             ret.append(self.GetItem(index, 0).m_text)
  530.             index = self.GetNextSelected(index)
  531.         return ret
  532.  
  533.  
  534.  
  535. class BaseHeader(BoxPanel):
  536.     
  537.     def __init__(self, parent, view, image_filename):
  538.         BoxPanel.__init__(self, parent, wx.VERTICAL)
  539.         mailicon = wx.Bitmap(image_filename, wx.BITMAP_TYPE_PNG)
  540.         self.list = ClassifierList(self, mailicon)
  541.         self.Add(self.list, 1, wx.EXPAND | wx.ALL, 0)
  542.         self.view = view
  543.         self.UpdateText()
  544.  
  545.  
  546.  
  547. class BaseView(SplitPanel):
  548.     
  549.     def __init__(self, parent, model, header_class, get_messages, drag_class, klass):
  550.         SplitPanel.__init__(self, parent, model)
  551.         self.header = header_class(self, self)
  552.         self.email = EmailView(self, model)
  553.         self.Add(self.header, 1, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 5)
  554.         self.Add(self.email, 1, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 5)
  555.         self.SetMinimumPaneSize(60)
  556.         model.AddView(self)
  557.         self.UpdateText()
  558.         self.get_messages = get_messages
  559.         self.drag_class = drag_class
  560.         self.klass = klass
  561.         self.list = self.header.list
  562.         self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.UpdateEmail)
  563.         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag)
  564.         self.list.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
  565.  
  566.     
  567.     def UpdateText(self):
  568.         self.header.UpdateText()
  569.         self.email.UpdateText()
  570.  
  571.     
  572.     def OnKillFocus(self, evt):
  573.         sort = (self.list._col, self.list._colSortFlag[self.list._col])
  574.         self.model.settings.sort_spam = sort
  575.         evt.Skip()
  576.  
  577.     
  578.     def OnBeginDrag(self, evt):
  579.         self.model.drag = self.drag_class
  580.         d = self.get_messages()
  581.         data = wx.CustomDataObject('SEDrag')
  582.         mail_dict = []([ (v[3], v[5]) for k, v in d.iteritems() ])
  583.         mail_addresses = [ (mail_dict[item[3]], item) for item in self.list.GetSelectedMsgs() ]
  584.         cargo = [
  585.             TYPE_EMAIL_MSGS] + mail_addresses
  586.         data.SetData(cPickle.dumps(cargo, 1))
  587.         dropSource = wx.DropSource(self.list)
  588.         dropSource.SetData(data)
  589.         result = dropSource.DoDragDrop(wx.Drag_AllowMove)
  590.  
  591.     
  592.     def UpdateEmail(self, evt):
  593.         if not self.list.selecting:
  594.             if self.list.GetSelectedMsgs() and hasattr(evt, 'm_itemIndex'):
  595.                 self.email.SetMsg(self.model.GetMessage(self.list.itemDataMap[self.list.GetItemData(evt.m_itemIndex)], self.klass))
  596.             else:
  597.                 self.email.SetMsg(self.model.GetMessage(None, self.klass))
  598.         
  599.  
  600.     
  601.     def UpdateView(self, model, sort):
  602.         if model.settings.split_vertically:
  603.             self.Unsplit(self.email)
  604.             self.SplitVertically(self.header, self.email, 0)
  605.         else:
  606.             self.Unsplit(self.email)
  607.             self.SplitHorizontally(self.header, self.email, 0)
  608.         mail_list = self.get_messages()
  609.         (self.list._col, self.list._colSortFlag[self.list._col]) = sort
  610.         update_data = { }
  611.         for k, v in mail_list.iteritems():
  612.             from_status = self.model.InAddressList((v[5],))[0]
  613.             if from_status:
  614.                 colour = None
  615.             elif from_status is False:
  616.                 colour = None
  617.             else:
  618.                 colour = None
  619.             update_data[k] = (v[0], v[1], v[2], v[3], colour)
  620.         
  621.         self.list.UpdateData(update_data)
  622.         self.UpdateEmail(None)
  623.  
  624.     
  625.     def RemoveFromList(self, evt):
  626.         self.model.RemoveFromList(self.list.GetSelectedMsgs(), self.klass)
  627.  
  628.     
  629.     def MoveToHam(self, evt):
  630.         msgs = self.list.GetSelectedMsgs()
  631.         current = self.list.GetFocusedItem()
  632.         s = self.get_messages()
  633.         spam_dict = []([ (v[3], v[5]) for k, v in s.iteritems() ])
  634.         for item in msgs:
  635.             if self.model.InBlacklist((spam_dict[item[3]],)).values()[0]:
  636.                 title = _("Warning: Address is in 'Blocked Senders'")
  637.                 msg = _("The message you are moving to 'Not Spam' is from an address (%s) that is in the 'Blocked Senders' list. Future email from this address will still be classified as 'spam'. Would you like to remove the address from the 'Blocked Senders' list?") % (item[0],)
  638.                 dlg = wx.MessageDialog(self, msg, title, wx.YES_NO)
  639.                 result = dlg.ShowModal()
  640.                 dlg.Destroy()
  641.                 if result == wx.ID_YES:
  642.                     self.model.DeleteFromBlacklist((spam_dict[item[3]],))
  643.                 
  644.             result == wx.ID_YES
  645.         
  646.         self.model.MoveToHam(self.klass, msgs)
  647.         self.list.Focus(current)
  648.         self.list.Select(current)
  649.         self.list.EnsureVisible(current)
  650.  
  651.     
  652.     def MoveToSpam(self, evt):
  653.         msgs = self.list.GetSelectedMsgs()
  654.         current = self.list.GetFocusedItem()
  655.         h = self.get_messages()
  656.         ham_dict = []([ (v[3], v[5]) for k, v in h.iteritems() ])
  657.         for item in msgs:
  658.             if self.model.InWhitelist((ham_dict[item[3]],)).values()[0]:
  659.                 title = _("Warning: Address is in 'Allowed Senders'")
  660.                 msg = _("The message you are moving to 'Spam' is from an address (%s) that is in the 'Allowed Senders' list. Future emails from this address will still be classified as 'not spam'. Would you like to remove the address from the 'Allowed Senders' list?") % (item[0],)
  661.                 dlg = wx.MessageDialog(self, msg, title, wx.YES_NO)
  662.                 result = dlg.ShowModal()
  663.                 dlg.Destroy()
  664.                 if result == wx.ID_YES:
  665.                     self.model.DeleteFromWhitelist((ham_dict[item[3]],))
  666.                 
  667.             result == wx.ID_YES
  668.         
  669.         self.model.MoveToSpam(self.klass, msgs)
  670.         self.list.Focus(current)
  671.         self.list.Select(current)
  672.         self.list.EnsureVisible(current)
  673.  
  674.     
  675.     def MoveToUnsure(self, evt):
  676.         msgs = self.list.GetSelectedMsgs()
  677.         current = self.list.GetFocusedItem()
  678.         h = self.get_messages()
  679.         unsure_dict = []([ (v[3], v[5]) for k, v in h.iteritems() ])
  680.         self.model.MoveToUnsure(self.klass, msgs)
  681.         self.list.Focus(current)
  682.         self.list.Select(current)
  683.         self.list.EnsureVisible(current)
  684.  
  685.     
  686.     def AddToWhitelist(self, evt):
  687.         d = self.get_messages()
  688.         mail_dict = []([ (v[3], v[5]) for k, v in d.iteritems() ])
  689.         []([ mail_dict[item[3]] for item in self.list.GetSelectedMsgs() ])
  690.  
  691.     
  692.     def AddToBlacklist(self, evt):
  693.         d = self.get_messages()
  694.         mail_dict = []([ (v[3], v[5]) for k, v in d.iteritems() ])
  695.         []([ mail_dict[item[3]] for item in self.list.GetSelectedMsgs() ])
  696.  
  697.  
  698.  
  699. class SpamHeader(BaseHeader):
  700.     
  701.     def __init__(self, parent, view):
  702.         BaseHeader.__init__(self, parent, view, get_image_filename('mail-icon-spam'))
  703.  
  704.     
  705.     def UpdateText(self):
  706.         self.SetHelpText(_("Emails on this page have been classified as 'spam'. If a mistake has been made please drag & drop the message to the 'Not Spam' button."))
  707.         self.list.menuData = [
  708.             (_("Move to 'Not Spam'"), self.view.MoveToHam),
  709.             (_("Move to 'Unsure'"), self.view.MoveToUnsure),
  710.             None,
  711.             (_("Add sender to 'Blocked Senders'"), self.view.AddToBlacklist),
  712.             (_("Add sender to 'Allowed Senders'"), self.view.AddToWhitelist),
  713.             None,
  714.             (_('Remove email(s) from list'), self.view.RemoveFromList),
  715.             None,
  716.             (_('Explain classification'), self.view.ShowEvidence)]
  717.         self.list.UpdateText()
  718.  
  719.  
  720.  
  721. class SpamView(BaseView):
  722.     
  723.     def __init__(self, parent, model):
  724.         BaseView.__init__(self, parent, model, SpamHeader, model.GetSpam, 'spam', IS_SPAM)
  725.  
  726.     
  727.     def OnFilter(self, filter_value):
  728.         if filter_value != self.model.filter_spam and not (self.model.updating):
  729.             self.model.filter_spam = filter_value
  730.             self.model.UpdateViews()
  731.         
  732.  
  733.     
  734.     def UpdateView(self, model):
  735.         sort = self.model.settings.sort_spam
  736.         BaseView.UpdateView(self, model, sort)
  737.  
  738.  
  739.  
  740. class UnsureHeader(BaseHeader):
  741.     
  742.     def __init__(self, parent, view):
  743.         BaseHeader.__init__(self, parent, view, get_image_filename('mail-icon-unsure'))
  744.  
  745.     
  746.     def UpdateText(self):
  747.         self.SetHelpText(_("SpamExperts was unable to correctly classify the emails found on this page. Please drag & drop the emails to the appropriate 'Spam'/'Not Spam' button."))
  748.         self.list.menuData = [
  749.             (_("Move to 'Not Spam'"), self.view.MoveToHam),
  750.             (_("Move to 'Spam'"), self.view.MoveToSpam),
  751.             None,
  752.             (_("Add sender to 'Blocked Senders'"), self.view.AddToBlacklist),
  753.             (_("Add sender to 'Allowed Senders'"), self.view.AddToWhitelist),
  754.             None,
  755.             (_('Remove email(s) from list'), self.view.RemoveFromList),
  756.             None,
  757.             (_('Explain classification'), self.view.ShowEvidence)]
  758.         self.list.UpdateText()
  759.  
  760.  
  761.  
  762. class UnsureView(BaseView):
  763.     
  764.     def __init__(self, parent, model):
  765.         BaseView.__init__(self, parent, model, UnsureHeader, model.GetUnsure, 'unsure', IS_UNSURE)
  766.  
  767.     
  768.     def OnFilter(self, filter_value):
  769.         if filter_value != self.model.filter_unsure and not (self.model.updating):
  770.             self.model.filter_unsure = filter_value
  771.             self.model.UpdateViews()
  772.         
  773.  
  774.     
  775.     def UpdateView(self, model):
  776.         sort = self.model.settings.sort_unsure
  777.         BaseView.UpdateView(self, model, sort)
  778.  
  779.  
  780.  
  781. class HamHeader(BaseHeader):
  782.     
  783.     def __init__(self, parent, view):
  784.         BaseHeader.__init__(self, parent, view, get_image_filename('mail-icon-ham'))
  785.  
  786.     
  787.     def UpdateText(self):
  788.         self.SetHelpText(_("Emails on this page have been classified as 'not spam'. If a mistake has been made please drag & drop the message to the 'Spam' button."))
  789.         self.list.menuData = [
  790.             (_("Move to 'Spam'"), self.view.MoveToSpam),
  791.             (_("Move to 'Unsure'"), self.view.MoveToUnsure),
  792.             None,
  793.             (_("Add sender to 'Blocked Senders'"), self.view.AddToBlacklist),
  794.             (_("Add sender to 'Allowed Senders'"), self.view.AddToWhitelist),
  795.             None,
  796.             (_('Remove email(s) from list'), self.view.RemoveFromList),
  797.             None,
  798.             (_('Explain classification'), self.view.ShowEvidence)]
  799.         self.list.UpdateText()
  800.  
  801.  
  802.  
  803. class HamView(BaseView):
  804.     
  805.     def __init__(self, parent, model):
  806.         BaseView.__init__(self, parent, model, HamHeader, model.GetHam, 'ham', IS_HAM)
  807.  
  808.     
  809.     def OnFilter(self, filter_value):
  810.         if filter_value != self.model.filter_ham and not (self.model.updating):
  811.             self.model.filter_ham = filter_value
  812.             self.model.UpdateViews()
  813.         
  814.  
  815.     
  816.     def UpdateView(self, model):
  817.         sort = self.model.settings.sort_ham
  818.         BaseView.UpdateView(self, model, sort)
  819.  
  820.  
  821.  
  822. class WhiteView(BoxPanel):
  823.     
  824.     def __init__(self, parent, model):
  825.         BoxPanel.__init__(self, parent, wx.VERTICAL)
  826.         help_style = wx.NO_BORDER | wx.BU_EXACTFIT
  827.         self.help_button = wx.ContextHelpButton(self, style = help_style)
  828.         helpicon = wx.Bitmap(get_image_filename('help-icon'), wx.BITMAP_TYPE_PNG)
  829.         self.help_button.SetBitmapLabel(helpicon)
  830.         self.Add(self.help_button, 0, wx.ALIGN_RIGHT | wx.ALL, 0)
  831.         mailicon = wx.Bitmap(get_image_filename('mail-icon-white'), wx.BITMAP_TYPE_PNG)
  832.         self.list = MailList(self, mailicon)
  833.         self.Add(self.list, 1, wx.EXPAND | wx.ALL, 3)
  834.         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag)
  835.         self.UpdateText()
  836.         model.AddView(self)
  837.         self.model = model
  838.  
  839.     
  840.     def UpdateText(self):
  841.         self.SetHelpText(_("On this screen you can find all senders that have been classified as 'allowed'.\n\nAny messages sent from these addresses will always be delivered to your inbox. To add an email address simply drag & drop an email from the 'Unsure', 'Not Spam' or 'Spam' list to the 'Allowed Senders' button on the left or right-click an email and select 'Move to Allowed Senders'.\n\nBy right-clicking on an empty space in the list you can manually add an email address. By right-clicking a listed email address you can remove or move it."))
  842.         self.list.menuData = [
  843.             (_("Move to 'Blocked Senders'"), self.MoveToBlacklist),
  844.             None,
  845.             (_('Add New Address'), self.AddAddress),
  846.             None,
  847.             (_('Delete'), self.Delete)]
  848.         self.list.UpdateText()
  849.  
  850.     
  851.     def OnBeginDrag(self, evt):
  852.         self.model.drag = 'white'
  853.         data = wx.CustomDataObject('SEDrag')
  854.         cargo = [
  855.             TYPE_EMAIL_ADDRESS] + self.list.GetSelectedEmails()
  856.         data.SetData(cPickle.dumps(cargo, 1))
  857.         dropSource = wx.DropSource(self.list)
  858.         dropSource.SetData(data)
  859.         result = dropSource.DoDragDrop(wx.Drag_AllowMove)
  860.  
  861.     
  862.     def UpdateView(self, model):
  863.         self.list.UpdateData(model.GetWhiteList())
  864.  
  865.     
  866.     def Delete(self, evt):
  867.         self.model.DeleteFromWhitelist(self.list.GetSelectedEmails())
  868.  
  869.     
  870.     def MoveToBlacklist(self, evt):
  871.         self.model.MoveToBlacklist(self.list.GetSelectedEmails())
  872.  
  873.     
  874.     def AddAddress(self, evt):
  875.         dlg = wx.TextEntryDialog(self, 'Email Address', 'Add to Whitelist')
  876.         if dlg.ShowModal() == wx.ID_OK:
  877.             newAddress = str(dlg.GetValue())
  878.             self.model.AddEmailToWhitelist(newAddress)
  879.         
  880.         dlg.Destroy()
  881.  
  882.  
  883.  
  884. class BlackView(BoxPanel):
  885.     
  886.     def __init__(self, parent, model):
  887.         BoxPanel.__init__(self, parent, wx.VERTICAL)
  888.         help_style = wx.NO_BORDER | wx.BU_EXACTFIT
  889.         self.help_button = wx.ContextHelpButton(self, style = help_style)
  890.         helpicon = wx.Bitmap(get_image_filename('help-icon'), wx.BITMAP_TYPE_PNG)
  891.         self.help_button.SetBitmapLabel(helpicon)
  892.         self.Add(self.help_button, 0, wx.ALIGN_RIGHT | wx.ALL, 0)
  893.         mailicon = wx.Bitmap(get_image_filename('mail-icon-black'), wx.BITMAP_TYPE_PNG)
  894.         self.list = MailList(self, mailicon)
  895.         self.Add(self.list, 1, wx.EXPAND | wx.ALL, 3)
  896.         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag)
  897.         self.UpdateText()
  898.         model.AddView(self)
  899.         self.model = model
  900.  
  901.     
  902.     def UpdateText(self):
  903.         self.SetHelpText(_("On this screen you can find all senders that have been classified as 'blocked'.\n\nAny messages sent from these addresses will always be blocked from arriving in your inbox. To add an email address simply drag & drop an email from the 'Unsure', 'Not Spam' or 'Spam' list to the 'Blocked Senders' button on the left or right-click an email and select 'Move to Blocked Senders'.\n\nBy right-clicking on an empty space in the list you can manually add an email address. By right-clicking a listed email address you can remove or move it."))
  904.         self.list.menuData = [
  905.             (_("Move to 'Allowed Senders'"), self.MoveToBlacklist),
  906.             None,
  907.             (_('Add New Address'), self.AddAddress),
  908.             None,
  909.             (_('Delete'), self.Delete)]
  910.         self.list.UpdateText()
  911.  
  912.     
  913.     def OnBeginDrag(self, evt):
  914.         self.model.drag = 'black'
  915.         data = wx.CustomDataObject('SEDrag')
  916.         cargo = [
  917.             TYPE_EMAIL_ADDRESS] + self.list.GetSelectedEmails()
  918.         data.SetData(cPickle.dumps(cargo, 1))
  919.         dropSource = wx.DropSource(self.list)
  920.         dropSource.SetData(data)
  921.         result = dropSource.DoDragDrop(wx.Drag_AllowMove)
  922.  
  923.     
  924.     def UpdateView(self, model):
  925.         self.list.UpdateData(model.GetBlackList())
  926.  
  927.     
  928.     def Delete(self, evt):
  929.         self.model.DeleteFromBlacklist(self.list.GetSelectedEmails())
  930.  
  931.     
  932.     def MoveToBlacklist(self, evt):
  933.         self.model.MoveToWhitelist(self.list.GetSelectedEmails())
  934.  
  935.     
  936.     def AddAddress(self, evt):
  937.         dlg = wx.TextEntryDialog(self, 'Email Address', 'Add to Blacklist')
  938.         if dlg.ShowModal() == wx.ID_OK:
  939.             newAddress = str(dlg.GetValue())
  940.             self.model.AddEmailToBlacklist(newAddress)
  941.         
  942.         dlg.Destroy()
  943.  
  944.  
  945.  
  946. class Classification(BoxPanel):
  947.     
  948.     def __init__(self, parent, model):
  949.         BoxPanel.__init__(self, parent, wx.HORIZONTAL)
  950.         self.model = model
  951.         self.buttonBar = ButtonBar(self, model)
  952.         self.spamView = SpamView(self, model)
  953.         self.unsureView = UnsureView(self, model)
  954.         self.hamView = HamView(self, model)
  955.         self.blackView = BlackView(self, model)
  956.         self.whiteView = WhiteView(self, model)
  957.         self.Add(self.buttonBar, 0, wx.EXPAND)
  958.         help_style = wx.NO_BORDER | wx.BU_EXACTFIT
  959.         self.help_button = wx.ContextHelpButton(self, style = help_style)
  960.         helpicon = wx.Bitmap(get_image_filename('help-icon'), wx.BITMAP_TYPE_PNG)
  961.         self.help_button.SetBitmapLabel(helpicon)
  962.         self.filter_label = wx.StaticText(self)
  963.         self.filter = wx.TextCtrl(self)
  964.         self.top_sizer = wx.BoxSizer(wx.HORIZONTAL)
  965.         self.top_sizer.Add(self.filter_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 0)
  966.         self.top_sizer.Add(self.filter, 1, wx.EXPAND | wx.LEFT, 2)
  967.         self.top_sizer.Add(self.help_button, 0, wx.ALIGN_RIGHT | wx.ALL, 0)
  968.         self.right_side = wx.BoxSizer(wx.VERTICAL)
  969.         self.right_side.Add(self.top_sizer, 0, wx.EXPAND | wx.ALL, 3)
  970.         self.right_side.Add(self.spamView, 1, wx.EXPAND)
  971.         self.right_side.Add(self.unsureView, 1, wx.EXPAND)
  972.         self.right_side.Add(self.hamView, 1, wx.EXPAND)
  973.         self.right_side.Add(self.blackView, 1, wx.EXPAND)
  974.         self.right_side.Add(self.whiteView, 1, wx.EXPAND)
  975.         self.Add(self.right_side, 1, wx.EXPAND)
  976.         self.views = (self.spamView, self.unsureView, self.hamView, self.blackView, self.whiteView)
  977.         self.filters = {
  978.             self.spamView: 'filter_spam',
  979.             self.hamView: 'filter_ham',
  980.             self.unsureView: 'filter_unsure' }
  981.         self.show_top = (self.spamView, self.unsureView, self.hamView)
  982.         self.active = None
  983.         model.AddView(self)
  984.         self.filter.Bind(wx.EVT_TEXT, self.OnFilter)
  985.  
  986.     
  987.     def OnFilter(self, unused):
  988.         active_view = self.views[self.active]
  989.         active_view.OnFilter(self.filter.GetValue())
  990.  
  991.     
  992.     def UpdateText(self):
  993.         self.filter_label.SetLabel(_('Search for:'))
  994.         self.filter_label.SetBestFittingSize()
  995.         self.filter_label.SetHelpText(_('Use this function to search for a specific sender and/or subject.'))
  996.         self.filter.SetHelpText(_('Use this function to search for a specific sender and/or subject.'))
  997.         self.buttonBar.UpdateText()
  998.         self.spamView.UpdateText()
  999.         self.hamView.UpdateText()
  1000.         self.blackView.UpdateText()
  1001.         self.whiteView.UpdateText()
  1002.         self.Layout()
  1003.  
  1004.     
  1005.     def UpdateView(self, model):
  1006.         if self.active != model.active:
  1007.             self.active = model.active
  1008.             for i, view in enumerate(self.views):
  1009.                 if i == self.active:
  1010.                     view.Show()
  1011.                     if view in self.show_top:
  1012.                         self.filter.Show()
  1013.                         self.filter_label.Show()
  1014.                         self.help_button.Show()
  1015.                     else:
  1016.                         self.filter.Hide()
  1017.                         self.filter_label.Hide()
  1018.                         self.help_button.Hide()
  1019.                 view in self.show_top
  1020.                 view.Hide()
  1021.             
  1022.             self.top_sizer.Layout()
  1023.             self.right_side.Layout()
  1024.             self.Layout()
  1025.         
  1026.         if self.views[self.active] in self.show_top:
  1027.             active_view = self.views[self.active]
  1028.             active_filter = self.filters[active_view]
  1029.             self.filter.SetValue(getattr(model, active_filter))
  1030.         
  1031.  
  1032.  
  1033.  
  1034. class InterceptedEdit(wx.Dialog):
  1035.     
  1036.     def __init__(self, parent, data):
  1037.         wx.Dialog.__init__(self, parent, title = _('Edit Ports and Description'))
  1038.         sizer = wx.GridBagSizer(0, 0)
  1039.         self.port = LeftLabeledTF(self, size = (60, -1), label = _('Ports'), validator = DigitsValidator())
  1040.         self.desc = LeftLabeledTF(self, size = (160, -1), label = _('Description'))
  1041.         self.port.SetValue(data[1])
  1042.         self.desc.SetValue(data[2])
  1043.         self.ok = wx.Button(self, wx.ID_OK)
  1044.         self.cancel = wx.Button(self, wx.ID_CANCEL)
  1045.         sizer.Add(self.port, (0, 0), (1, 1))
  1046.         sizer.Add(self.desc, (0, 2), (1, 3))
  1047.         sizer.Add(self.ok, (1, 1), (1, 1))
  1048.         sizer.Add(self.cancel, (1, 2), (1, 1))
  1049.         self.SetSizerAndFit(sizer)
  1050.  
  1051.     
  1052.     def UpdateText(self):
  1053.         pass
  1054.  
  1055.  
  1056.  
  1057. class InterceptedApps(BoxPanel):
  1058.     
  1059.     def __init__(self, parent, model):
  1060.         BoxPanel.__init__(self, parent)
  1061.         self.add = wx.Button(self, label = '')
  1062.         self.remove = wx.Button(self, label = '')
  1063.         self.remove.Enable(False)
  1064.         self.list = AutoWidthSortedLC(self, style = wx.LC_SINGLE_SEL | wx.LC_REPORT | wx.LC_SORT_ASCENDING | wx.LC_VRULES | wx.LC_HRULES)
  1065.         self.Add(self.list, 2, wx.EXPAND | wx.ALL, 5)
  1066.         rSizer = wx.BoxSizer(wx.HORIZONTAL)
  1067.         rSizer.Add(self.add, 0, wx.ALL, 5)
  1068.         rSizer.Add(self.remove, 0, wx.ALL, 5)
  1069.         self.Add(rSizer, 0, wx.ALIGN_RIGHT, 5)
  1070.         self.add.Bind(wx.EVT_BUTTON, self.OnAdd)
  1071.         self.remove.Bind(wx.EVT_BUTTON, self.OnRemove)
  1072.         self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelect)
  1073.         self.list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnSelect)
  1074.         self.list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnActivate)
  1075.         self.UpdateView(model)
  1076.         self.UpdateText()
  1077.         model.AddView(self)
  1078.         self.model = model
  1079.  
  1080.     
  1081.     def UpdateText(self):
  1082.         self.SetTitle(_('Intercepted email clients'))
  1083.         self.add.SetTitle(_('Add'))
  1084.         self.add.SetBestFittingSize()
  1085.         self.remove.SetTitle(_('Remove'))
  1086.         self.remove.SetBestFittingSize()
  1087.         self.SetHelpText(_('Intercepted email clients is a list of executables that will be automatically intercepted by SpamExperts.\n\nIf you would like to exclude an email client from being filtered, please remove it from the list. If your email client is not in the list, please add the executable.'))
  1088.  
  1089.     
  1090.     def OnActivate(self, evt):
  1091.         old = self.list.itemDataMap[self.list.GetItemData(evt.m_itemIndex)]
  1092.         dlg = InterceptedEdit(self, old)
  1093.         if dlg.ShowModal() == wx.ID_OK:
  1094.             new = [
  1095.                 old[0],
  1096.                 dlg.port.GetValue(),
  1097.                 dlg.desc.GetValue()]
  1098.             if old != new:
  1099.                 self.GetParent().GetParent().apply.Enable(True)
  1100.                 index = self.list.GetItemData(evt.m_itemIndex)
  1101.                 self.list.itemDataMap[index] = (new[0], new[1], new[2])
  1102.                 self.UpdateData(self.list.itemDataMap)
  1103.             
  1104.         
  1105.         dlg.Destroy()
  1106.  
  1107.     
  1108.     def OnSelect(self, evt):
  1109.         index = self.list.GetFirstSelected()
  1110.         self.remove.Enable(index != -1)
  1111.  
  1112.     
  1113.     def OnAdd(self, evt):
  1114.         wildcardSpec = 'Executable files (*.exe)|*.exe'
  1115.         dialog = wx.FileDialog(self, _('Select email client executable to intercept'), wildcard = wildcardSpec, style = wx.OPEN)
  1116.         if dialog.ShowModal() == wx.ID_OK:
  1117.             path = dialog.GetPath()
  1118.             data = self.model.GetInterceptedAppData(path)
  1119.             if data not in self.list.itemDataMap.values():
  1120.                 key = max(self.list.itemDataMap.keys()) + 1
  1121.                 self.list.itemDataMap[key] = data
  1122.                 index = self.list.InsertImageStringItem(sys.maxint, data[0], 2)
  1123.                 self.list.SetStringItem(index, 1, data[1])
  1124.                 self.list.SetStringItem(index, 2, data[2])
  1125.                 self.list.SetItemData(index, key)
  1126.                 self.GetParent().GetParent().apply.Enable(True)
  1127.             
  1128.         
  1129.         dialog.Destroy()
  1130.  
  1131.     
  1132.     def OnRemove(self, evt):
  1133.         index = self.list.GetFirstSelected()
  1134.         if index != -1:
  1135.             sel = self.list.GetItemData(index)
  1136.             self.list.DeleteItem(index)
  1137.             self.list.itemDataMap.pop(sel)
  1138.         
  1139.         self.GetParent().GetParent().apply.Enable(True)
  1140.  
  1141.     
  1142.     def UpdateView(self, model):
  1143.         data = model.settings.mailapps
  1144.         app_dict = dict(enumerate(data))
  1145.         self.UpdateData(app_dict)
  1146.  
  1147.     
  1148.     def UpdateData(self, data):
  1149.         self.columns = [
  1150.             (_('Executable'), 150),
  1151.             (_('Port'), 60),
  1152.             (_('Application Name'), 250)]
  1153.         self.list.itemDataMap = data
  1154.         self.list.ClearAll()
  1155.         self.ResetColumns()
  1156.         for key, data in self.list.itemDataMap.items():
  1157.             index = self.list.InsertImageStringItem(sys.maxint, data[0], 2)
  1158.             self.list.SetStringItem(index, 1, str(data[1]))
  1159.             self.list.SetStringItem(index, 2, data[2])
  1160.             self.list.SetItemData(index, key)
  1161.         
  1162.         for i in xrange(3):
  1163.             self.list.SetColumnWidth(i, wx.LIST_AUTOSIZE)
  1164.         
  1165.  
  1166.     
  1167.     def ResetColumns(self):
  1168.         for column, width in self.columns:
  1169.             idx = self.columns.index((column, width))
  1170.             self.list.InsertColumn(idx, column)
  1171.         
  1172.  
  1173.     
  1174.     def GetValue(self):
  1175.         return self.list.itemDataMap.values()
  1176.  
  1177.  
  1178.  
  1179. class PeriodicRetrieval(BoxPanel):
  1180.     
  1181.     def __init__(self, parent, model):
  1182.         wx.Panel.__init__(self, parent)
  1183.         style = wx.LC_SINGLE_SEL | wx.LC_REPORT | wx.LC_SORT_ASCENDING | wx.LC_VRULES | wx.LC_HRULES
  1184.         self.model = model
  1185.         self.initial_dns_lookup = False
  1186.         self.list_box = wx.StaticBox(self, -1, '')
  1187.         self.list_box_sizer = wx.StaticBoxSizer(self.list_box, wx.VERTICAL)
  1188.         self.list = AutoWidthSortedLC(self, style = style)
  1189.         self.list_box_sizer.Add(self.list, 2, wx.EXPAND | wx.ALL, 5)
  1190.         self.remove = wx.Button(self, label = '')
  1191.         self.remove.Enable(False)
  1192.         self.list_box_sizer.Add(self.remove, 0, wx.ALIGN_RIGHT, 10)
  1193.         self.not_list_box = wx.StaticBox(self, -1, '')
  1194.         self.not_list_box_sizer = wx.StaticBoxSizer(self.not_list_box, wx.VERTICAL)
  1195.         self.not_list = AutoWidthSortedLC(self, style = style)
  1196.         self.not_list_box_sizer.Add(self.not_list, 2, wx.EXPAND | wx.ALL, 5)
  1197.         self.not_remove = wx.Button(self, label = '')
  1198.         self.not_remove.Enable(False)
  1199.         self.not_list_box_sizer.Add(self.not_remove, 0, wx.ALIGN_RIGHT, 10)
  1200.         border = wx.BoxSizer(wx.VERTICAL)
  1201.         border.Add(self.list_box_sizer, 1, wx.EXPAND | wx.ALL, 5)
  1202.         border.Add(self.not_list_box_sizer, 1, wx.EXPAND | wx.ALL, 5)
  1203.         self.SetSizer(border)
  1204.         self.UpdateView(model)
  1205.         self.UpdateText()
  1206.         model.AddView(self)
  1207.         self.remove.Bind(wx.EVT_BUTTON, self.OnRemove)
  1208.         self.not_remove.Bind(wx.EVT_BUTTON, self.OnNotRemove)
  1209.         self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelect)
  1210.         self.list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnSelect)
  1211.         self.list.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginPeriodicDrag)
  1212.         self.not_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnNotSelect)
  1213.         self.not_list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnNotSelect)
  1214.         self.not_list.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginNotPeriodicDrag)
  1215.         self.list.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
  1216.         self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp)
  1217.         self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
  1218.         self.not_list.Bind(wx.EVT_RIGHT_DOWN, self.OnNotRightDown)
  1219.         self.not_list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnNotRightUp)
  1220.         self.not_list.Bind(wx.EVT_RIGHT_UP, self.OnNotRightUp)
  1221.         self.target = ListDropTarget(self.OnDropPeriod)
  1222.         self.not_target = ListDropTarget(self.OnDropNotPeriod)
  1223.         self.list.SetDropTarget(self.target)
  1224.         self.not_list.SetDropTarget(self.not_target)
  1225.         self.not_list.menuData = [
  1226.             (_('Check Periodically'), self.MoveToPeriodic),
  1227.             (_('Remove'), self.OnNotRemove)]
  1228.         self.list.menuData = [
  1229.             (_("Don't Check Periodically"), self.MoveToNotPeriodic),
  1230.             (_('Remove'), self.OnRemove)]
  1231.  
  1232.     
  1233.     def MoveToPeriodic(self, unused):
  1234.         index = self.not_list.GetFirstSelected()
  1235.         if index != -1:
  1236.             cargo = ((index, self.not_list.itemDataMap[self.not_list.GetItemData(index)]),)
  1237.             self.OnDropPeriod(cargo)
  1238.         
  1239.  
  1240.     
  1241.     def MoveToNotPeriodic(self, unused):
  1242.         index = self.list.GetFirstSelected()
  1243.         if index != -1:
  1244.             cargo = ((index, self.list.itemDataMap[self.list.GetItemData(index)]),)
  1245.             self.OnDropNotPeriod(cargo)
  1246.         
  1247.  
  1248.     
  1249.     def RightDown(self, evt, ctrl):
  1250.         self.pos = evt.GetPosition()
  1251.         (item, flags) = ctrl.HitTest(self.pos)
  1252.         if flags & wx.LIST_HITTEST_ONITEM:
  1253.             if not ctrl.GetItemState(item, wx.LIST_STATE_SELECTED):
  1254.                 ctrl.DeselectAll()
  1255.                 ctrl.Select(item)
  1256.             
  1257.         
  1258.         evt.Skip()
  1259.  
  1260.     
  1261.     def RightUp(self, evt, ctrl):
  1262.         (item, flags) = ctrl.HitTest(self.pos)
  1263.         if flags & wx.LIST_HITTEST_ONITEM:
  1264.             menu = List2Menu(ctrl, ctrl.menuData)
  1265.             ctrl.PopupMenu(menu, self.pos)
  1266.         
  1267.         evt.Skip()
  1268.  
  1269.     
  1270.     def OnRightDown(self, evt):
  1271.         self.RightDown(evt, self.list)
  1272.  
  1273.     
  1274.     def OnRightUp(self, evt):
  1275.         self.RightUp(evt, self.list)
  1276.  
  1277.     
  1278.     def OnNotRightDown(self, evt):
  1279.         self.RightDown(evt, self.not_list)
  1280.  
  1281.     
  1282.     def OnNotRightUp(self, evt):
  1283.         self.RightUp(evt, self.not_list)
  1284.  
  1285.     
  1286.     def OnBeginDrag(self, list_control):
  1287.         data = wx.CustomDataObject('SEListDrag')
  1288.         cargo = []
  1289.         index = list_control.GetFirstSelected()
  1290.         while index != -1:
  1291.             cargo.append((index, list_control.itemDataMap[list_control.GetItemData(index)]))
  1292.             index = list_control.GetNextSelected(index)
  1293.         data.SetData(cPickle.dumps(cargo, cPickle.HIGHEST_PROTOCOL))
  1294.         dropSource = wx.DropSource(list_control)
  1295.         dropSource.SetData(data)
  1296.         dropSource.DoDragDrop(wx.Drag_AllowMove)
  1297.  
  1298.     
  1299.     def OnBeginPeriodicDrag(self, evt):
  1300.         self.not_target.match = True
  1301.         self.target.match = False
  1302.         self.OnBeginDrag(self.list)
  1303.  
  1304.     
  1305.     def OnBeginNotPeriodicDrag(self, evt):
  1306.         self.target.match = True
  1307.         self.not_target.match = False
  1308.         self.OnBeginDrag(self.not_list)
  1309.  
  1310.     
  1311.     def OnDrop(self, cargo, from_, to):
  1312.         for index, data in cargo:
  1313.             sel = from_.GetItemData(index)
  1314.             from_.DeleteItem(index)
  1315.             from_.itemDataMap.pop(sel)
  1316.             host = dnslookup.lookup[data[0]]
  1317.             index = to.InsertImageStringItem(sys.maxint, host, 2)
  1318.             to.SetStringItem(index, 1, data[2])
  1319.             key = len(to.itemDataMap)
  1320.             to.SetItemData(index, key)
  1321.             to.itemDataMap[key] = data
  1322.         
  1323.         self.GetParent().GetParent().apply.Enable(True)
  1324.  
  1325.     
  1326.     def OnDropPeriod(self, cargo):
  1327.         cargo = [ (i, c[:3] + ('',) + c[-2:]) for i, c in cargo ]
  1328.         self.OnDrop(cargo, self.not_list, self.list)
  1329.  
  1330.     
  1331.     def OnDropNotPeriod(self, cargo):
  1332.         cargo = [ (i, c[:3] + c[-2:]) for i, c in cargo ]
  1333.         self.OnDrop(cargo, self.list, self.not_list)
  1334.  
  1335.     
  1336.     def UpdateText(self):
  1337.         self.list_box.SetTitle(_('Connections to retrieve mail from'))
  1338.         self.not_list_box.SetTitle(_('Connections to not retrieve mail from'))
  1339.         self.remove.SetTitle(_('Remove'))
  1340.         self.remove.SetBestFittingSize()
  1341.         self.not_remove.SetTitle(_('Remove'))
  1342.         self.not_remove.SetBestFittingSize()
  1343.         self.SetHelpText(_("When 'Periodic retrieval' is enabled, SpamExperts will automatically check for new emails at the set interval. Whenever 'not spam' emails have been retrieved, it will inform you."))
  1344.  
  1345.     
  1346.     def OnSelect(self, evt):
  1347.         index = self.list.GetFirstSelected()
  1348.         self.remove.Enable(index != -1)
  1349.  
  1350.     
  1351.     def OnNotSelect(self, evt):
  1352.         index = self.not_list.GetFirstSelected()
  1353.         self.not_remove.Enable(index != -1)
  1354.  
  1355.     
  1356.     def OnRemove(self, evt):
  1357.         index = self.list.GetFirstSelected()
  1358.         if index != -1:
  1359.             sel = self.list.GetItemData(index)
  1360.             self.list.DeleteItem(index)
  1361.             self.list.itemDataMap.pop(sel)
  1362.         
  1363.         self.GetParent().GetParent().apply.Enable(True)
  1364.  
  1365.     
  1366.     def OnNotRemove(self, evt):
  1367.         index = self.not_list.GetFirstSelected()
  1368.         if index != -1:
  1369.             sel = self.not_list.GetItemData(index)
  1370.             self.not_list.DeleteItem(index)
  1371.             self.not_list.itemDataMap.pop(sel)
  1372.         
  1373.         self.GetParent().GetParent().apply.Enable(True)
  1374.  
  1375.     
  1376.     def UpdateView(self, model):
  1377.         remove_dict = dict(enumerate(model.settings.periodic))
  1378.         not_dict = dict(enumerate(model.settings.not_periodic))
  1379.         self.UpdateData(remove_dict, not_dict)
  1380.  
  1381.     
  1382.     def UpdateData(self, data, not_data):
  1383.         self.columns = [
  1384.             (_('Server'), 250),
  1385.             (_('Username'), 150)]
  1386.         self.list.itemDataMap = data
  1387.         self.list.ClearAll()
  1388.         self.not_list.itemDataMap = not_data
  1389.         self.not_list.ClearAll()
  1390.         self.ResetColumns()
  1391.         for ctrl in (self.list, self.not_list):
  1392.             for key, data in ctrl.itemDataMap.items():
  1393.                 if self.initial_dns_lookup:
  1394.                     d = dnslookup.lookup[data[0]]
  1395.                 else:
  1396.                     d = data[0]
  1397.                 index = ctrl.InsertImageStringItem(sys.maxint, d, 2)
  1398.                 ctrl.SetStringItem(index, 1, data[2])
  1399.                 ctrl.SetItemData(index, key)
  1400.             
  1401.             if ctrl.itemDataMap:
  1402.                 for i in xrange(2):
  1403.                     ctrl.SetColumnWidth(i, wx.LIST_AUTOSIZE)
  1404.                 
  1405.             for unused, size in enumerate(self.columns):
  1406.                 ctrl.SetColumnWidth(i, size)
  1407.             
  1408.         
  1409.         if not self.initial_dns_lookup:
  1410.             t = threading.Thread(target = self.FillInDNS)
  1411.             t.start()
  1412.         
  1413.  
  1414.     
  1415.     def FillInDNS(self):
  1416.         if self.initial_dns_lookup:
  1417.             return None
  1418.         
  1419.         dns = { }
  1420.         for ctrl in (self.list, self.not_list):
  1421.             for key, d in ctrl.itemDataMap.items():
  1422.                 dnslookup.lookup[d[0]]
  1423.             
  1424.         
  1425.         self.initial_dns_lookup = True
  1426.  
  1427.     
  1428.     def ResetColumns(self):
  1429.         for title, unused in enumerate(self.columns):
  1430.             self.list.InsertColumn(i, title)
  1431.             self.not_list.InsertColumn(i, title)
  1432.         
  1433.  
  1434.     
  1435.     def GetValue(self):
  1436.         return (self.list.itemDataMap.values(), self.not_list.itemDataMap.values())
  1437.  
  1438.  
  1439.  
  1440. class Settings(BoxPanel):
  1441.     
  1442.     def __init__(self, parent, model, tray):
  1443.         BoxPanel.__init__(self, parent)
  1444.         self.block = wx.CheckBox(self, label = '')
  1445.         self.modSubj = wx.CheckBox(self, label = '')
  1446.         self.tag = wx.TextCtrl(self)
  1447.         self.lang = LeftLabeledChoice(self, '', choices = tray.languages.values())
  1448.         
  1449.         try:
  1450.             language = tray.languages[model.settings.lang]
  1451.         except KeyError:
  1452.             print >>sys.stderr, 'Unknown language,', model.settings.lang, '(using English).'
  1453.             language = 'English'
  1454.  
  1455.         self.lang.SetStringSelection(language)
  1456.         self.date_header = LeftLabeledChoice(self, '', choices = ('Delivery-Date', 'Date'))
  1457.         self.date_header.SetStringSelection(model.GetSettings().date_header)
  1458.         self.on_startup = wx.CheckBox(self, label = '')
  1459.         self.vertical = wx.CheckBox(self, label = '')
  1460.         self.user_address = wx.TextCtrl(self)
  1461.         self.auto_bugreport = wx.CheckBox(self, label = '')
  1462.         self.auto_update = wx.CheckBox(self, label = '')
  1463.         self.browse = wx.Button(self, label = '')
  1464.         self.notify_sound = LeftLabeledTF(self, '')
  1465.         self.cache_expiry_days = LeftLabeledTF(self, size = (40, -1), validator = DigitValidator())
  1466.         self.enable_period = wx.CheckBox(self, label = '')
  1467.         self.period = wx.TextCtrl(self, size = (40, -1), validator = DigitValidator())
  1468.         self.enable_balloons = wx.CheckBox(self, label = '')
  1469.         self.sizers = [
  1470.             self.GetSizer()]
  1471.         self.Add(self.block, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1472.         self.subj_sizer = wx.BoxSizer(wx.HORIZONTAL)
  1473.         self.subj_sizer.Add(self.modSubj, 0, wx.ALIGN_CENTRE | wx.ALIGN_LEFT | wx.ALL, 0)
  1474.         self.subj_sizer.Add(self.tag, 1, wx.ALL | wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT, 0)
  1475.         self.sizers.append(self.subj_sizer)
  1476.         self.Add(self.subj_sizer, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 5)
  1477.         self.Add(self.on_startup, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1478.         self.Add(self.auto_update, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1479.         self.report_sizer = wx.BoxSizer(wx.HORIZONTAL)
  1480.         self.report_sizer.Add(self.auto_bugreport, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, 0)
  1481.         flags = wx.TOP | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL
  1482.         self.report_sizer.Add(self.user_address, 1, flags, 2)
  1483.         self.sizers.append(self.report_sizer)
  1484.         self.Add(self.report_sizer, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 5)
  1485.         self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 3)
  1486.         self.Add(self.vertical, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1487.         self.Add(self.lang, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1488.         self.Add(self.date_header, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1489.         self.sound = wx.BoxSizer(wx.HORIZONTAL)
  1490.         self.sound.Add(self.notify_sound, 1, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 0)
  1491.         self.sound.Add(self.browse, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, 2)
  1492.         self.sizers.append(self.sound)
  1493.         self.Add(self.sound, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 5)
  1494.         self.Add(self.cache_expiry_days, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1495.         self.period_sizer = wx.BoxSizer(wx.HORIZONTAL)
  1496.         self.period_sizer.Add(self.enable_period, 0, wx.ALIGN_CENTER | wx.ALIGN_LEFT | wx.ALL, 0)
  1497.         self.period_sizer.Add(self.period, 0, wx.ALIGN_CENTER | wx.ALIGN_RIGHT | wx.ALL, 0)
  1498.         self.sizers.append(self.period_sizer)
  1499.         self.Add(self.period_sizer, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1500.         self.Add(self.enable_balloons, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1501.         self.block.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1502.         self.modSubj.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1503.         self.tag.Bind(wx.EVT_TEXT, self.UpdateApply)
  1504.         self.cache_expiry_days.tf.Bind(wx.EVT_TEXT, self.UpdateApply)
  1505.         self.lang.ch.Bind(wx.EVT_CHOICE, self.UpdateApply)
  1506.         self.date_header.ch.Bind(wx.EVT_CHOICE, self.UpdateApply)
  1507.         self.auto_bugreport.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1508.         self.auto_update.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1509.         self.user_address.Bind(wx.EVT_TEXT, self.UpdateApply)
  1510.         self.on_startup.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1511.         self.vertical.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1512.         self.browse.Bind(wx.EVT_BUTTON, self.OnBrowse)
  1513.         self.period.Bind(wx.EVT_TEXT, self.UpdateApply)
  1514.         self.notify_sound.tf.Bind(wx.EVT_TEXT, self.UpdateApply)
  1515.         self.enable_period.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1516.         self.enable_balloons.Bind(wx.EVT_CHECKBOX, self.UpdateApply)
  1517.         self.model = model
  1518.         self.tray = tray
  1519.         self.UpdateText()
  1520.         model.AddView(self)
  1521.  
  1522.     
  1523.     def UpdateText(self):
  1524.         self.block.SetLabel(_('Block Spam'))
  1525.         self.block.SetBestFittingSize()
  1526.         self.block.SetHelpText(_("When 'Block Spam' has been selected, email recognized as 'spam' will not arrive in your email client. Note that you will have to check your email twice from your email client in order to receive the filtered email. Every check is always one round behind so the 'spam' can be filtered and the email can be prepared to be released to the email client. When using 'Periodic Retrieval' this will be handled at the background."))
  1527.         self.modSubj.SetLabel(_('Modify subject, using tag'))
  1528.         self.modSubj.SetBestFittingSize()
  1529.         self.modSubj.SetHelpText(_("Please select this option to modify the subject of emails detected as 'spam' using the tag on the right."))
  1530.         self.tag.SetBestFittingSize()
  1531.         self.tag.SetHelpText(_("When 'Modify Subject' has been selected, the text specified on the right will be added to the subject of email recognized as 'spam'. This way you can easily recognize what has been detected as 'spam', and you can create a rule in your email client to move the detected email to a specified folder based on its subject."))
  1532.         self.lang.SetLabel(_('Language:'))
  1533.         self.lang.SetBestFittingSize()
  1534.         self.notify_sound.SetLabel(_('New email sound notification: '))
  1535.         self.notify_sound.SetBestFittingSize()
  1536.         self.notify_sound.SetHelpText(_("SpamExperts can play a sound when new 'not spam' email has arrived."))
  1537.         self.cache_expiry_days.SetLabel(_('Cache expiry time (days)'))
  1538.         self.cache_expiry_days.SetBestFittingSize()
  1539.         self.cache_expiry_days.SetHelpText(_('Here you can set the amount of days after which SpamExperts will automatically delete the emails from its internal cache. This does not influence the training.'))
  1540.         self.browse.SetTitle(_('Browse'))
  1541.         self.browse.SetBestFittingSize()
  1542.         self.auto_update.SetLabel(_('Automatically update'))
  1543.         self.auto_update.SetBestFittingSize()
  1544.         self.auto_update.SetHelpText(_('This will automatically update SpamExperts whenever an update is available.'))
  1545.         self.auto_bugreport.SetLabel(_('Automatically send bug reports from email address'))
  1546.         self.auto_bugreport.SetBestFittingSize()
  1547.         self.auto_bugreport.SetHelpText(_('This will allow SpamExperts to automatically send a bug report when a crash occurs.'))
  1548.         self.user_address.SetBestFittingSize()
  1549.         self.user_address.SetHelpText(_('Please enter your email address so we will be able to contact you with a possible solution.'))
  1550.         self.date_header.SetLabel(_('Date sorting type:'))
  1551.         self.date_header.SetBestFittingSize()
  1552.         self.date_header.SetHelpText(_('Different email clients use a different date sorting method. When the SpamExperts date format does not match your email client please choose the alternative sorting format.'))
  1553.         self.on_startup.SetLabel(_('Launch SpamExperts on login'))
  1554.         self.on_startup.SetBestFittingSize()
  1555.         self.on_startup.SetHelpText(_('Automatically start SpamExperts when Windows is loaded.'))
  1556.         self.vertical.SetLabel(_('Display email list and content side-by-side'))
  1557.         self.vertical.SetBestFittingSize()
  1558.         self.vertical.SetHelpText(_('This option allows you to switch between a side-by-side or a top-bottom display of the email list and their contents.'))
  1559.         self.enable_period.SetLabel(_('Periodically check for email every (minutes)'))
  1560.         self.enable_period.SetBestFittingSize()
  1561.         self.enable_period.SetHelpText(_("When 'Periodic retrieval' is enabled, SpamExperts will automatically check for new emails at the set interval. Whenever 'not spam' emails have been retrieved, it will inform you."))
  1562.         self.period.SetHelpText(_("When 'Periodic retrieval' is enabled, SpamExperts will automatically check for new emails at the set interval. Whenever 'not spam' emails have been retrieved, it will inform you."))
  1563.         self.enable_balloons.SetLabel(_('Display balloon notifications'))
  1564.         self.enable_balloons.SetBestFittingSize()
  1565.         self.enable_balloons.SetHelpText(_("When there is new 'not spam' email, SpamExperts can inform you with a balloon notification."))
  1566.         self.AdjustReportSizer()
  1567.         for sizer in self.sizers:
  1568.             sizer.Layout()
  1569.         
  1570.  
  1571.     
  1572.     def AdjustReportSizer(self):
  1573.         self.report_sizer.Layout()
  1574.         if self.report_sizer.GetOrientation() == wx.HORIZONTAL:
  1575.             if self.user_address.GetSize()[0] < self.user_address.GetSize()[0]:
  1576.                 pass
  1577.             elif self.user_address.GetSize()[0] < 100:
  1578.                 self.report_sizer.SetOrientation(wx.VERTICAL)
  1579.                 self.report_sizer.Remove(1)
  1580.                 flags = wx.TOP | wx.EXPAND | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL
  1581.                 self.report_sizer.Add(self.user_address, 0, flags, 2)
  1582.             else:
  1583.                 self.report_sizer.SetOrientation(wx.HORIZONTAL)
  1584.                 self.report_sizer.Remove(1)
  1585.                 flags = wx.TOP | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL
  1586.                 self.report_sizer.Add(self.user_address, 1, flags, 2)
  1587.         
  1588.         self.GetSizer().Fit(self)
  1589.         grandparent = self.GetParent().GetParent()
  1590.         grandparent.GetSizer().Fit(grandparent)
  1591.  
  1592.     
  1593.     def UpdateView(self, model):
  1594.         settings = model.GetSettings()
  1595.         self.block.SetValue(settings.block_spam)
  1596.         self.modSubj.SetValue(settings.modify_subject)
  1597.         self.tag.SetValue(settings.modify_subject_tag)
  1598.         self.user_address.SetValue(settings.user_address)
  1599.         self.auto_bugreport.SetValue(settings.auto_bugreport)
  1600.         self.auto_update.SetValue(settings.automatically_update)
  1601.         self.on_startup.SetValue(settings.launch_on_startup)
  1602.         self.vertical.SetValue(settings.split_vertically)
  1603.         self.notify_sound.SetValue(settings.notify_sound)
  1604.         self.cache_expiry_days.SetValue(str(options[('Storage', 'cache_expiry_days')]))
  1605.         self.enable_period.SetValue(model.settings.enable_periodic)
  1606.         self.period.SetValue(str(model.settings.period))
  1607.         self.enable_balloons.SetValue(settings.enable_balloon_notifications)
  1608.  
  1609.     
  1610.     def OnBrowse(self, evt):
  1611.         wildcard = 'Wave files (*.wav)|*.wav|All files (*.*)|*.*'
  1612.         default_dir = os.path.join(application_directory(), 'sounds')
  1613.         dlg = wx.FileDialog(self, message = 'Choose a sound file', defaultDir = default_dir, defaultFile = '', wildcard = wildcard, style = wx.OPEN)
  1614.         if dlg.ShowModal() == wx.ID_OK:
  1615.             paths = dlg.GetPaths()
  1616.             self.notify_sound.SetValue(paths[0])
  1617.         
  1618.         dlg.Destroy()
  1619.  
  1620.     
  1621.     def UpdateApply(self, unused):
  1622.         apply = self.GetParent().GetParent().apply
  1623.         if self.cache_expiry_days.GetValue() != str(options[('Storage', 'cache_expiry_days')]):
  1624.             apply.Enable(True)
  1625.             return None
  1626.         
  1627.         settings = [
  1628.             (self.block.GetValue(), 'block_spam'),
  1629.             (self.modSubj.GetValue(), 'modify_subject'),
  1630.             (self.tag.GetValue(), 'modify_subject_tag'),
  1631.             (self.auto_bugreport.GetValue(), 'auto_bugreport'),
  1632.             (self.user_address.GetValue(), 'user_address'),
  1633.             (self.date_header.GetSelection(), 'date_header'),
  1634.             (self.auto_update.GetValue(), 'automatically_update'),
  1635.             (self.on_startup.GetValue(), 'launch_on_startup'),
  1636.             (self.vertical.GetValue(), 'split_vertically'),
  1637.             (self.enable_period.GetValue(), 'enable_periodic'),
  1638.             (self.notify_sound.GetValue(), 'notify_sound'),
  1639.             (self.enable_balloons.GetValue(), 'enable_balloon_notifications')]
  1640.         
  1641.         try:
  1642.             period = float(self.period.GetValue())
  1643.         except ValueError:
  1644.             period = self.period.GetValue()
  1645.  
  1646.         settings.append((period, 'period'))
  1647.         for code, name in self.tray.languages.iteritems():
  1648.             if name == self.lang.GetSelection():
  1649.                 settings.append((code, 'lang'))
  1650.                 break
  1651.                 continue
  1652.         
  1653.         old_settings = self.model.settings
  1654.         apply.Enable(False)
  1655.         for new_setting, att in settings:
  1656.             if getattr(old_settings, att) != new_setting:
  1657.                 apply.Enable(True)
  1658.                 break
  1659.                 continue
  1660.         
  1661.  
  1662.  
  1663.  
  1664. class LSPStatus(BoxPanel):
  1665.     
  1666.     def __init__(self, parent, model):
  1667.         BoxPanel.__init__(self, parent, wx.HORIZONTAL)
  1668.         self.stat = wx.StaticText(self)
  1669.         self.status_label = wx.StaticText(self, label = '')
  1670.         self.Add(self.status_label, 0, wx.ALIGN_CENTER)
  1671.         self.Add(self.stat)
  1672.         self.UpdateText()
  1673.         model.AddView(self)
  1674.  
  1675.     
  1676.     def UpdateText(self):
  1677.         self.SetHelpText(_("When this status is 'On', the LSP is able to intercept email connections."))
  1678.         self.status_label.SetLabel(_('LSP Status: '))
  1679.         self.status_label.SetBestFittingSize()
  1680.  
  1681.     
  1682.     def UpdateView(self, model):
  1683.         if model.lsp_on:
  1684.             self.stat.SetLabel(_('ON'))
  1685.             self.stat.SetForegroundColour('black')
  1686.         else:
  1687.             self.stat.SetLabel(_('OFF'))
  1688.             self.stat.SetForegroundColour('red')
  1689.         self.GetParent().GetSizer().Layout()
  1690.  
  1691.  
  1692.  
  1693. class AboutSE(BoxPanel):
  1694.     
  1695.     def __init__(self, parent, model):
  1696.         BoxPanel.__init__(self, parent)
  1697.         self.copyright = wx.StaticText(self, label = '', style = wx.ALIGN_CENTER)
  1698.         self.model = model
  1699.         self.credits = wx.StaticText(self, label = '', style = wx.ALIGN_CENTER)
  1700.         self.translation_credits = wx.StaticText(self, label = '', style = wx.ALIGN_CENTER)
  1701.         self.Add((-1, 10))
  1702.         self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 20)
  1703.         self.Add(self.credits, 0, wx.ALIGN_CENTER)
  1704.         self.Add((-1, 10))
  1705.         self.Add(self.translation_credits, 0, wx.ALIGN_CENTER)
  1706.         self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 20)
  1707.         self.Add(self.copyright, 0, wx.ALIGN_CENTER)
  1708.         self.Add((-1, 10))
  1709.         self.support = LabeledHyperlink(self, '', 'mailto:support@spamexperts.com', 'mailto:support@spamexperts.com')
  1710.         self.Add(self.support, 0, wx.ALIGN_CENTER)
  1711.         self.website = LabeledHyperlink(self, '', 'http://www.spamexperts.com', 'http://www.spamexperts.com')
  1712.         self.Add(self.website, 0, wx.ALIGN_CENTER)
  1713.         self.UpdateText()
  1714.  
  1715.     
  1716.     def UpdateText(self):
  1717.         copyright = _('Copyright (C) 2006 SpamExperts V.O.F. All rights reserved.')
  1718.         self.copyright.SetLabel(copyright)
  1719.         self.copyright.SetBestFittingSize()
  1720.         credits = _('Programming credits to:\nPaul Boots, Peter Damoc, Paul Jansen,\nNenad Nikolic, Barak Weichselbaum, Stuart Welch, Tony Meyer')
  1721.         self.credits.SetLabel(credits)
  1722.         self.credits.SetBestFittingSize()
  1723.         translation = _('Translation credits to:\nSergey Burkov, Krzysztof Zielinski, Andrea Chavarro Manotas')
  1724.         self.translation_credits.SetLabel(translation)
  1725.         self.translation_credits.SetBestFittingSize()
  1726.         self.support.SetLabel(_('Support: '))
  1727.         self.support.SetBestFittingSize()
  1728.         self.website.SetLabel(_('Website: '))
  1729.         self.website.SetBestFittingSize()
  1730.  
  1731.  
  1732.  
  1733. class Status(BoxPanel):
  1734.     
  1735.     def __init__(self, parent, model):
  1736.         BoxPanel.__init__(self, parent)
  1737.         self.lsp_status = LSPStatus(self, model)
  1738.         self.model = model
  1739.         self.stats = wx.StaticBox(self)
  1740.         self.real_stats_sizer = wx.StaticBoxSizer(self.stats, wx.VERTICAL)
  1741.         self.stats_sizer = wx.BoxSizer(wx.VERTICAL)
  1742.         self.real_stats_sizer.Add(self.stats_sizer, 1, wx.EXPAND)
  1743.         self.Add(self.lsp_status, 0, wx.ALIGN_CENTER)
  1744.         self.sizer.AddSpacer((-1, 10))
  1745.         self.Add(self.real_stats_sizer, 1, wx.ALIGN_CENTER | wx.EXPAND)
  1746.         self.reset_stats = wx.Button(self)
  1747.         self.reset_stats.Bind(wx.EVT_BUTTON, self.OnReset)
  1748.         self.controls = []
  1749.         self.UpdateText()
  1750.  
  1751.     
  1752.     def UpdateText(self):
  1753.         self.reset_stats.SetLabel(_('Reset statistics'))
  1754.         self.reset_stats.SetBestFittingSize()
  1755.         self.reset_stats.SetHelpText(_('Resetting statistics does not influence any training data.'))
  1756.         self.stats.SetLabel(_('Performance Statistics'))
  1757.         while True:
  1758.             
  1759.             try:
  1760.                 self.stats_sizer.Detach(0)
  1761.             continue
  1762.             except wx.PyAssertionError:
  1763.                 break
  1764.                 continue
  1765.             
  1766.  
  1767.             None<EXCEPTION MATCH>wx.PyAssertionError
  1768.         for control in self.controls:
  1769.             
  1770.             try:
  1771.                 control.Hide()
  1772.             except TypeError:
  1773.                 pass
  1774.  
  1775.             control.Destroy()
  1776.         
  1777.         self.controls = []
  1778.         if hasattr(self.model.state, 'statistics'):
  1779.             stats = self.model.state.statistics.GetStats()
  1780.         else:
  1781.             print >>sys.stderr, "Can't display statistics before starting up."
  1782.             stats = ()
  1783.         for stat, help in stats:
  1784.             if help == help:
  1785.                 pass
  1786.             elif help == '':
  1787.                 control = wx.StaticLine(self)
  1788.             else:
  1789.                 control = wx.BoxSizer(wx.HORIZONTAL)
  1790.                 parts = stat.split('\t')
  1791.                 for i, part in enumerate(parts):
  1792.                     part_control = wx.StaticText(self, label = part)
  1793.                     part_control.SetHelpText(help)
  1794.                     part_control.SetBestFittingSize()
  1795.                     if i == 0:
  1796.                         align = wx.ALIGN_LEFT
  1797.                     elif i == len(parts) - 1:
  1798.                         align = wx.ALIGN_RIGHT
  1799.                     else:
  1800.                         align = wx.ALIGN_CENTER
  1801.                     control.Add(part_control, 1, wx.EXPAND | align)
  1802.                 
  1803.             self.stats_sizer.Add(control, 0, wx.EXPAND | wx.ALL, 3)
  1804.             self.controls.append(control)
  1805.         
  1806.         self.stats_sizer.AddSpacer((-1, 10))
  1807.         self.stats_sizer.Add(self.reset_stats, 0, wx.ALIGN_CENTER | wx.ALL, 3)
  1808.         self.stats.SetHelpText(_('Information about how well SpamExperts is performing.'))
  1809.         self.stats_sizer.Layout()
  1810.         self.sizer.Layout()
  1811.         self.SetBestFittingSize()
  1812.         self.stats.Refresh()
  1813.         self.stats.Update()
  1814.         self.Refresh()
  1815.         self.Update()
  1816.         self.lsp_status.UpdateText()
  1817.  
  1818.     
  1819.     def OnReset(self, unused):
  1820.         if options[('globals', 'verbose')]:
  1821.             print 'Resetting performance statistics'
  1822.         
  1823.         if hasattr(self.model.state, 'statistics'):
  1824.             self.model.state.statistics.Reset()
  1825.             self.model.state.statistics.ResetTotal(True)
  1826.         else:
  1827.             print >>sys.stderr, "Can't reset stats before starting up."
  1828.         self.UpdateText()
  1829.         self.GetParent().SetBestFittingSize()
  1830.  
  1831.  
  1832.  
  1833. class ProCodeDialog(wx.Dialog):
  1834.     
  1835.     def __init__(self, parent, ID, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE):
  1836.         wx.Dialog.__init__(self, parent, ID, title, pos, size, style)
  1837.         sizer = wx.BoxSizer(wx.VERTICAL)
  1838.         label = wx.StaticText(self, -1, _('Please enter your code:'))
  1839.         sizer.Add(label, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1840.         box = wx.BoxSizer(wx.HORIZONTAL)
  1841.         self.one = wx.TextCtrl(self, -1, '', size = (45, -1))
  1842.         self.one.Bind(wx.EVT_TEXT, (lambda x: self.MoveOn(self.one, self.two)))
  1843.         box.Add(self.one, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
  1844.         self.two = wx.TextCtrl(self, -1, '', size = (45, -1))
  1845.         self.two.Bind(wx.EVT_TEXT, (lambda x: self.MoveOn(self.two, self.three)))
  1846.         box.Add(self.two, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
  1847.         self.three = wx.TextCtrl(self, -1, '', size = (45, -1))
  1848.         self.three.Bind(wx.EVT_TEXT, (lambda x: self.MoveOn(self.three, self.four)))
  1849.         box.Add(self.three, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
  1850.         self.four = wx.TextCtrl(self, -1, '', size = (45, -1))
  1851.         self.four.SetMaxLength(5)
  1852.         box.Add(self.four, 1, wx.ALIGN_CENTRE | wx.ALL, 5)
  1853.         sizer.Add(box, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
  1854.         line = wx.StaticLine(self, -1, size = (20, -1), style = wx.LI_HORIZONTAL)
  1855.         sizer.Add(line, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP, 5)
  1856.         btnsizer = wx.StdDialogButtonSizer()
  1857.         btn = wx.Button(self, wx.ID_OK)
  1858.         btn.SetDefault()
  1859.         btnsizer.AddButton(btn)
  1860.         btn = wx.Button(self, wx.ID_CANCEL)
  1861.         btnsizer.AddButton(btn)
  1862.         btnsizer.Realize()
  1863.         sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.ALL, 5)
  1864.         self.SetSizer(sizer)
  1865.         sizer.Fit(self)
  1866.  
  1867.     
  1868.     def MoveOn(self, cur, next):
  1869.         if len(cur.GetValue()) >= 5:
  1870.             next.SetFocus()
  1871.             if len(cur.GetValue()) > 5:
  1872.                 next.SetValue(cur.GetValue()[5:].replace('-', ''))
  1873.                 cur.SetValue(cur.GetValue()[:5])
  1874.             
  1875.         
  1876.         if len(self.four.GetValue()) == 5:
  1877.             self.four.SetFocus()
  1878.             self.four.SetInsertionPoint(5)
  1879.         
  1880.  
  1881.     
  1882.     def Code(self):
  1883.         return '-'.join((self.one.GetValue(), self.two.GetValue(), self.three.GetValue(), self.four.GetValue()))
  1884.  
  1885.  
  1886.  
  1887. class WelcomeDialog(wx.Dialog):
  1888.     
  1889.     def __init__(self, parent, settings):
  1890.         title = _('Welcome to SpamExperts')
  1891.         pre = wx.PreDialog()
  1892.         pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
  1893.         pre.Create(None, -1, title = title)
  1894.         self.PostCreate(pre)
  1895.         self.settings = settings
  1896.         sizer = wx.BoxSizer(wx.VERTICAL)
  1897.         important = wx.StaticText(self, -1, _('Important'))
  1898.         
  1899.         try:
  1900.             font = wx.Font(-1, wx.DEFAULT, wx.NORMAL, wx.BOLD)
  1901.         except wx.PyAssertionError:
  1902.             font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.BOLD)
  1903.  
  1904.         important.SetFont(font)
  1905.         training_msg = _("In order to effectively use this software, and enable its self-learning capabilities, we recommend you correctly classify a minimum of 10 'spam' and 10 'not spam' messages.\n\nAll incoming messages will be displayed in the 'Spam', 'Not Spam' or 'Unsure' list. You should always move the messages from the 'Unsure' list to the appropriate 'Spam' or 'Not Spam' button. Whenever SpamExperts has made a classification mistake, you should correct this by moving the message to the appropriate button.\n\nIf you have any questions about this software feel free to contact us.")
  1906.         training_msg = fill(training_msg, 80)
  1907.         training = wx.StaticText(self, -1, training_msg)
  1908.         periodic_msg = _("When 'periodic retrieval' is enabled, SpamExperts will automatically check your account(s) for new email (default every 5 minutes). Whenever 'not spam' email has been retrieved you will be informed.")
  1909.         periodic_msg = fill(periodic_msg, 80)
  1910.         periodic = wx.StaticText(self, -1, periodic_msg)
  1911.         enable_periodic = wx.CheckBox(self, -1, _('Enable periodic retrieval'))
  1912.         self.enable_periodic = enable_periodic
  1913.         enable_periodic.SetValue(self.settings.enable_periodic)
  1914.         enable_periodic.Bind(wx.EVT_CHECKBOX, self.on_enable_periodic)
  1915.         enable_periodic.SetHelpText(_("When 'periodic retrieval' is enabled, SpamExperts will automatically check your account(s) for new email (default every 5 minutes). Whenever 'not spam' email has been retrieved you will be informed."))
  1916.         show = wx.CheckBox(self, -1, _('Show this dialog on startup'))
  1917.         show.SetValue(self.settings.show_welcome)
  1918.         show.Bind(wx.EVT_CHECKBOX, self.on_show_on_startup)
  1919.         show.SetHelpText(_('If you untick this box, this dialog will no longer display when SpamExperts starts.'))
  1920.         self.SetBackgroundColour('white')
  1921.         training_box = wx.StaticBox(self, -1, _('Training Tip'))
  1922.         training_box_sizer = wx.StaticBoxSizer(training_box, wx.VERTICAL)
  1923.         periodic_box = wx.StaticBox(self, -1, _('Periodic Retrieval'))
  1924.         periodic_box_sizer = wx.StaticBoxSizer(periodic_box, wx.VERTICAL)
  1925.         training_box_sizer.Add(important, 0, wx.ALIGN_CENTER | wx.ALL, 5)
  1926.         training_box_sizer.Add(training, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1927.         periodic_box_sizer.Add(periodic, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1928.         periodic_box_sizer.Add(enable_periodic, 0, wx.ALIGN_LEFT | wx.ALL, 5)
  1929.         sizer.Add(training_box_sizer, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
  1930.         sizer.Add(periodic_box_sizer, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
  1931.         sizer.Add(show, 0, wx.ALIGN_RIGHT | wx.ALL, 10)
  1932.         training_box.SetFocus()
  1933.         self.SetSizer(sizer)
  1934.         sizer.Fit(self)
  1935.  
  1936.     
  1937.     def on_show_on_startup(self, unused):
  1938.         self.settings.show_welcome = not (self.settings.show_welcome)
  1939.  
  1940.     
  1941.     def on_enable_periodic(self, unused):
  1942.         self.settings.enable_periodic = self.enable_periodic.GetValue()
  1943.  
  1944.  
  1945.  
  1946. class MainFrame(wx.Frame):
  1947.     CLOSE_ID = 101
  1948.     EXIT_ID = 102
  1949.     CONFIGURE_ID = 201
  1950.     RESET_ID = 202
  1951.     ABOUT_ID = 301
  1952.     STATUS_ID = 302
  1953.     SUBMIT_ID = 303
  1954.     PROFESSIONAL_ID = 304
  1955.     CHECK_ID = 305
  1956.     FAQ_ID = 306
  1957.     INSTRUCTIONS_ID = 307
  1958.     UPGRADE_ID = 308
  1959.     
  1960.     def __init__(self, model, tray):
  1961.         if license.get_code():
  1962.             title = _('SpamExperts Professional')
  1963.         else:
  1964.             title = _('SpamExperts Home')
  1965.         wx.Frame.__init__(self, None, -1, title = title, style = wx.CAPTION | wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX | wx.CLOSE_BOX)
  1966.         self.SetBackgroundColour('white')
  1967.         self.SetMinSize((100, 250))
  1968.         sizer = wx.BoxSizer(wx.VERTICAL)
  1969.         self.SetSizer(sizer)
  1970.         logo = wx.Image(get_image_filename('logo', tray.lang)).ConvertToBitmap()
  1971.         self.logo = wx.StaticBitmap(self, bitmap = logo)
  1972.         sizer.Add(self.logo, 0, wx.ALIGN_CENTER | wx.ALL, 3)
  1973.         sizer.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 0)
  1974.         self.cls = Classification(self, model)
  1975.         sizer.Add(self.cls, 2, wx.EXPAND | wx.ALL, 3)
  1976.         self.Bind(EVT_SUBMIT_REPORT, self.submit_report)
  1977.         self.Bind(wx.EVT_CLOSE, self.HandleClose)
  1978.         self.Bind(EVT_EXIT_MODEL, self.ExitModel)
  1979.         self.Bind(EVT_DO_UPDATE, self.DoUpdate)
  1980.         self.Bind(EVT_CONCLUDE_UPDATE, self.ConcludeUpdate)
  1981.         self.Bind(EVT_DO_WELCOME, self.ShowWelcome)
  1982.         self.Bind(EVT_PRO_BUTTON, self.EnterProKey)
  1983.         self.tray = tray
  1984.         self.model = model
  1985.         self.lang = None
  1986.         self.exit = False
  1987.         self.submitting = False
  1988.         self.entering_key = False
  1989.         self.pro_dialog = None
  1990.         self.welcome = None
  1991.         model.AddView(self)
  1992.         self.SetSize((600, 600))
  1993.         menuBar = wx.MenuBar()
  1994.         fileMenu = wx.Menu()
  1995.         fileMenu.Append(self.CLOSE_ID, '', '')
  1996.         fileMenu.Append(self.EXIT_ID, '', '')
  1997.         menuBar.Append(fileMenu, '')
  1998.         editMenu = wx.Menu()
  1999.         editMenu.Append(self.CONFIGURE_ID, '', '')
  2000.         editMenu.AppendSeparator()
  2001.         editMenu.Append(self.RESET_ID, '', '')
  2002.         menuBar.Append(editMenu, '')
  2003.         helpMenu = wx.Menu()
  2004.         helpMenu.Append(self.FAQ_ID, '', '')
  2005.         helpMenu.Append(self.INSTRUCTIONS_ID, '', '')
  2006.         helpMenu.AppendSeparator()
  2007.         helpMenu.Append(self.STATUS_ID, '', '')
  2008.         helpMenu.Append(self.SUBMIT_ID, '', '')
  2009.         helpMenu.AppendSeparator()
  2010.         helpMenu.Append(self.CHECK_ID, '', '')
  2011.         helpMenu.AppendSeparator()
  2012.         if license.get_code():
  2013.             helpMenu.Append(self.PROFESSIONAL_ID, '', '')
  2014.         else:
  2015.             helpMenu.Append(self.UPGRADE_ID, '', '')
  2016.         helpMenu.Append(self.ABOUT_ID, '', '')
  2017.         menuBar.Append(helpMenu, '')
  2018.         self.SetMenuBar(menuBar)
  2019.         self.CreateStatusBar()
  2020.         self.Bind(wx.EVT_MENU, self.Hide, id = self.CLOSE_ID)
  2021.         self.Bind(wx.EVT_MENU, self.OnExit, id = self.EXIT_ID)
  2022.         self.Bind(wx.EVT_MENU, self.tray.OnConfigure, id = self.CONFIGURE_ID)
  2023.         self.Bind(wx.EVT_MENU, self.tray.OnStatus, id = self.STATUS_ID)
  2024.         self.Bind(wx.EVT_MENU, self.tray.OnAbout, id = self.ABOUT_ID)
  2025.         self.Bind(wx.EVT_MENU, self.OnSubmitReport, id = self.SUBMIT_ID)
  2026.         self.Bind(wx.EVT_MENU, self.OnProButton, id = self.PROFESSIONAL_ID)
  2027.         self.Bind(wx.EVT_MENU, self.OnReset, id = self.RESET_ID)
  2028.         self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, id = self.CHECK_ID)
  2029.         self.Bind(wx.EVT_MENU, self.OnFAQ, id = self.FAQ_ID)
  2030.         self.Bind(wx.EVT_MENU, self.OnInstructions, id = self.INSTRUCTIONS_ID)
  2031.         self.Bind(wx.EVT_MENU, self.OnUpgrade, id = self.UPGRADE_ID)
  2032.         self.UpdateText()
  2033.  
  2034.     
  2035.     def OnFAQ(self, unused):
  2036.         '''Open link to FAQ page.'''
  2037.         webbrowser.open('http://faq.spamexperts.com', True, True)
  2038.  
  2039.     
  2040.     def OnInstructions(self, unused):
  2041.         '''Open link to Basic Instructions page.'''
  2042.         webbrowser.open('http://basicinstructions.spamexperts.com', True, True)
  2043.  
  2044.     
  2045.     def ShowWelcome(self, unused):
  2046.         '''Display welcome message to the user.'''
  2047.         self.welcome = WelcomeDialog(self, self.model.settings)
  2048.         self.welcome.Show()
  2049.  
  2050.     
  2051.     def DoUpdate(self, unused):
  2052.         if hasattr(self.tray.update_thread, 'cancel'):
  2053.             self.tray.update_thread.cancel()
  2054.         
  2055.         self.tray.update_thread = None
  2056.         progress_style = wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE | wx.PD_SMOOTH
  2057.         dlg = wx.ProgressDialog(_('SpamExperts Update'), _('Starting update.'), maximum = 100, style = progress_style)
  2058.         updater = software_update.CompleteUpdate(self.tray)
  2059.         
  2060.         try:
  2061.             updater.balloon = self.tray.UpdateOnClose
  2062.         except AttributeError:
  2063.             print >>sys.stderr, 'Previous update failed.  Need to replace SpamExperts.exe ASAP.'
  2064.  
  2065.         updater.update(progress_update_wrapper(dlg))
  2066.         dlg.Destroy()
  2067.  
  2068.     
  2069.     def ConcludeUpdate(self, unused):
  2070.         progress_style = wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_SMOOTH
  2071.         dlg = wx.ProgressDialog(_('SpamExperts Update'), _('Concluding update.'), maximum = 100, style = progress_style)
  2072.         updater = software_update.CompleteUpdate(self.tray)
  2073.         updater.conclude_update(progress_update_wrapper(dlg))
  2074.         dlg.Destroy()
  2075.  
  2076.     
  2077.     def OnReset(self, evt):
  2078.         dlg = wx.MessageDialog(self, _('Warning! When you reset the user data all saved information, such as training information and emails from the lists will be lost.\nDo you want to continue?'), _('Reset SpamExperts user data'), wx.ICON_WARNING | wx.YES_NO)
  2079.         if dlg.ShowModal() == wx.ID_YES:
  2080.             self.model.ResetAll()
  2081.             self.model.UpdateViews(refresh = True)
  2082.         
  2083.         dlg.Destroy()
  2084.  
  2085.     
  2086.     def EnterProKey(self, unused):
  2087.         self.entering_key = True
  2088.         while True:
  2089.             result = self.OnProButton()
  2090.             if result is None:
  2091.                 self.OnExit(None)
  2092.                 break
  2093.             
  2094.             if result is True:
  2095.                 break
  2096.                 continue
  2097.         self.entering_key = False
  2098.  
  2099.     
  2100.     def OnUpgrade(self, unused = None):
  2101.         self.OnProButton()
  2102.  
  2103.     
  2104.     def OnProButton(self, unused = None):
  2105.         '''Returns True if successful, False if not successful, and None
  2106.         if cancelled.'''
  2107.         d = ProCodeDialog(self, -1, _('Enter professional code'))
  2108.         self.pro_dialog = d
  2109.         result = d.ShowModal()
  2110.         code = d.Code()
  2111.         d.Destroy()
  2112.         self.pro_dialog = None
  2113.         if result == wx.ID_OK:
  2114.             license = license
  2115.             import spamexperts
  2116.             old_code = license.get_code()
  2117.             license.store_code(code)
  2118.             if not license.check_license_validity():
  2119.                 msg = _('Your license is invalid.')
  2120.                 d = wx.MessageDialog(None, msg, _('Invalid license'), wx.OK | wx.ICON_ERROR)
  2121.                 d.ShowModal()
  2122.                 d.Destroy()
  2123.                 if old_code is None:
  2124.                     license.remove_code()
  2125.                 else:
  2126.                     license.store_code(old_code)
  2127.                 return False
  2128.             
  2129.             data_dir = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
  2130.             license.update_license()
  2131.             if time.time() > license.get_expiry():
  2132.                 msg = _('Your license has expired and must be renewed.')
  2133.                 d = wx.MessageDialog(None, msg, _('Invalid license'), wx.OK | wx.ICON_ERROR)
  2134.                 d.ShowModal()
  2135.                 d.Destroy()
  2136.                 if old_code is None:
  2137.                     license.remove_code()
  2138.                 else:
  2139.                     license.store_code(old_code)
  2140.                 return False
  2141.             
  2142.             msg = _('Thank you. Registration complete.')
  2143.             d = wx.MessageDialog(None, msg, _('Valid license'), wx.OK)
  2144.             d.ShowModal()
  2145.             d.Destroy()
  2146.             return True
  2147.         
  2148.  
  2149.     
  2150.     def OnSubmitReport(self, unused):
  2151.         import error_reporter
  2152.         dlg = wx.ProgressDialog(_('SpamExperts'), _('Sending...'), maximum = 100)
  2153.         (succeeded, reason) = error_reporter.ErrorReporter().submit_report(progress_update_wrapper(dlg))
  2154.         dlg.Destroy()
  2155.         if not succeeded:
  2156.             dlg = wx.MessageDialog(self, _('Could not send error report: ') + reason, _('SpamExperts Error'), wx.OK | wx.ICON_ERROR)
  2157.             dlg.ShowModal()
  2158.             dlg.Destroy()
  2159.         
  2160.  
  2161.     
  2162.     def submit_report(self, unused):
  2163.         self.submitting = True
  2164.         dlg = wx.ProgressDialog(_('SpamExperts'), _('Sending...'), maximum = 100)
  2165.         import error_reporter
  2166.         errorReporter = error_reporter.ErrorReporter()
  2167.         (succeeded, reason) = errorReporter.submit_report(progress_update_wrapper(dlg))
  2168.         dlg.Destroy()
  2169.         if not succeeded:
  2170.             dlg = wx.MessageDialog(self, _('Could not send error report: ') + reason, _('SpamExperts Error'), wx.OK | wx.ICON_ERROR)
  2171.             dlg.ShowModal()
  2172.             dlg.Destroy()
  2173.         
  2174.         self.submitting = False
  2175.  
  2176.     
  2177.     def OnExit(self, evt):
  2178.         if self.welcome:
  2179.             if options[('globals', 'verbose')]:
  2180.                 print 'Closing welcome'
  2181.             
  2182.             self.welcome.Hide()
  2183.             self.welcome.Close()
  2184.             self.welcome.Destroy()
  2185.         
  2186.         if self.pro_dialog:
  2187.             self.pro_dialog.Hide()
  2188.             self.pro_dialog.Close()
  2189.             self.pro_dialog.Destroy()
  2190.         
  2191.         self.tray.PostError(None)
  2192.  
  2193.     
  2194.     def UpdateView(self, model):
  2195.         if self.lang != model.settings.lang:
  2196.             self.tray.SetupLanguage(model.settings.lang)
  2197.             self.UpdateText()
  2198.             self.lang = model.settings.lang
  2199.         
  2200.  
  2201.     
  2202.     def Hide(self, event = None):
  2203.         wx.Frame.Hide(self)
  2204.  
  2205.     
  2206.     def Show(self, hide = False):
  2207.         if hide:
  2208.             return self.Hide()
  2209.         
  2210.         self.model.UpdateViews(True)
  2211.         self.Raise()
  2212.         wx.Frame.Show(self)
  2213.  
  2214.     
  2215.     def UpdateText(self):
  2216.         self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO))
  2217.         logo = wx.Image(get_image_filename('logo', self.tray.lang)).ConvertToBitmap()
  2218.         self.logo.SetBitmap(logo)
  2219.         mb = self.GetMenuBar()
  2220.         item = mb.FindItemById(self.CLOSE_ID)
  2221.         item.SetText(_('&Close'))
  2222.         item.SetHelp(_('Close'))
  2223.         item = mb.FindItemById(self.EXIT_ID)
  2224.         item.SetText(_('E&xit'))
  2225.         item.SetHelp(_('Exit SpamExperts'))
  2226.         item = mb.FindItemById(self.CONFIGURE_ID)
  2227.         item.SetText(_('&Settings'))
  2228.         item.SetHelp(_('Configure SpamExperts'))
  2229.         item = mb.FindItemById(self.RESET_ID)
  2230.         item.SetText(_('Reset SpamExperts User Data'))
  2231.         item.SetHelp(_('Resetting your user data allows you to continue the use of SpamExperts with a fresh training and email database. Note that SpamExperts will require new training on email to be able to properly recognize spam.'))
  2232.         item = mb.FindItemById(self.STATUS_ID)
  2233.         item.SetText(_('&Status'))
  2234.         item.SetHelp(_('Status and Statistics'))
  2235.         item = mb.FindItemById(self.FAQ_ID)
  2236.         item.SetText(_('&Open FAQ Page'))
  2237.         item.SetHelp(_('Open a new browser window to the SpamExperts Frequently Asked Questions (FAQ) page.'))
  2238.         item = mb.FindItemById(self.INSTRUCTIONS_ID)
  2239.         item.SetText(_('Open &Basic Instructions'))
  2240.         item.SetHelp(_('Open a new browser window to a page of basic instructions for SpamExperts.'))
  2241.         item = mb.FindItemById(self.ABOUT_ID)
  2242.         item.SetText(_('&About SpamExperts'))
  2243.         item.SetHelp(_('About SpamExperts and Status'))
  2244.         item = mb.FindItemById(self.SUBMIT_ID)
  2245.         item.SetText(_('S&ubmit Bug Report'))
  2246.         item.SetHelp(_('Submit bug report'))
  2247.         if license.get_code():
  2248.             item = mb.FindItemById(self.PROFESSIONAL_ID)
  2249.             if not item:
  2250.                 item = mb.FindItemById(self.UPGRADE_ID)
  2251.                 self.Bind(wx.EVT_MENU, self.OnProButton, id = self.UPGRADE_ID)
  2252.             
  2253.             item.SetText(_('E&nter new license key'))
  2254.             item.SetHelp(_('Enter new license key for SpamExperts Professional'))
  2255.         else:
  2256.             item = mb.FindItemById(self.UPGRADE_ID)
  2257.             if not item:
  2258.                 item = mb.FindItemById(self.PROFESSIONAL_ID)
  2259.                 self.Bind(wx.EVT_MENU, self.OnUpgrade, id = self.PROFESSIONAL_ID)
  2260.             
  2261.             item.SetText(_('&Upgrade to Professional'))
  2262.             item.SetHelp(_('Enter license key to upgrade to SpamExperts Professional'))
  2263.         item = mb.FindItemById(self.CHECK_ID)
  2264.         item.SetText(_('&Check for SpamExperts Updates'))
  2265.         item.SetHelp(_("SpamExperts checks for updates whenever it is launched, but if you can't wait until then, you can manually check via this menu item."))
  2266.         mb.SetLabelTop(0, _('&File'))
  2267.         mb.SetLabelTop(1, _('&Edit'))
  2268.         mb.SetLabelTop(2, _('&Help'))
  2269.         self.cls.UpdateText()
  2270.  
  2271.     
  2272.     def OnCheckForUpdate(self, unused):
  2273.         if self.model.isUpdateAvailable():
  2274.             if self.tray.update_thread == 'in_progress':
  2275.                 dlg = wx.MessageDialog(self, _('Update already in progress.'), _('SpamExperts'), wx.OK)
  2276.                 dlg.ShowModal()
  2277.                 dlg.Destroy()
  2278.                 return None
  2279.             
  2280.             
  2281.             try:
  2282.                 self.tray.update_thread.cancel()
  2283.             except AttributeError:
  2284.                 pass
  2285.  
  2286.             self.tray.update_thread = 'manual'
  2287.             self.tray.CheckForUpdate()
  2288.         else:
  2289.             dlg = wx.MessageDialog(self, _('SpamExperts is already up to date.'), _('SpamExperts'), wx.OK)
  2290.             dlg.ShowModal()
  2291.             dlg.Destroy()
  2292.  
  2293.     
  2294.     def OnCheckMessages(self, event = None):
  2295.         if self.entering_key:
  2296.             return None
  2297.         
  2298.         self.Show()
  2299.         self.Restore()
  2300.         self.Raise()
  2301.         self.model.active = VIEW_UNSURE
  2302.         self.cls.UpdateView(self.model)
  2303.         self.cls.buttonBar.UpdateView(self.model)
  2304.  
  2305.     
  2306.     def HandleClose(self, unused = None):
  2307.         self.Hide()
  2308.         if self.exit:
  2309.             self.tray.PostError('exit')
  2310.             import thread
  2311.             thread.exit()
  2312.         
  2313.  
  2314.     
  2315.     def ExitModel(self, unused = None):
  2316.         self.Hide()
  2317.         if self.welcome:
  2318.             if options[('globals', 'verbose')]:
  2319.                 print 'Closing welcome'
  2320.             
  2321.             self.welcome.Hide()
  2322.             self.welcome.Close()
  2323.             self.welcome.Destroy()
  2324.         
  2325.         dlg = wx.ProgressDialog(_('SpamExperts'), _('Closing...'), maximum = 100)
  2326.         self.model.Exit(progress_update_wrapper(dlg))
  2327.         dlg.Destroy()
  2328.         self.exit = True
  2329.         self.HandleClose()
  2330.  
  2331.  
  2332.  
  2333. class SettingsDialog(wx.Dialog):
  2334.     
  2335.     def __init__(self, model, tray):
  2336.         if license.get_code():
  2337.             title = _('SpamExperts Professional Configuration')
  2338.         else:
  2339.             title = _('SpamExperts Home Configuration')
  2340.         pre = wx.PreDialog()
  2341.         pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
  2342.         pre.Create(None, -1, title = title)
  2343.         self.PostCreate(pre)
  2344.         self.nb = wx.Notebook(self)
  2345.         sizer = wx.BoxSizer(wx.VERTICAL)
  2346.         sizer.Add(self.nb, 1, wx.EXPAND | wx.ALL, 3)
  2347.         self.SetSizer(sizer)
  2348.         self.apply = wx.Button(self, label = '')
  2349.         self.setting = Settings(self.nb, model, tray)
  2350.         self.intercepted = InterceptedApps(self.nb, model)
  2351.         self.periodic = PeriodicRetrieval(self.nb, model)
  2352.         self.nb.AddPage(self.setting, '')
  2353.         self.nb.AddPage(self.intercepted, '')
  2354.         self.nb.AddPage(self.periodic, '')
  2355.         self.status_message = wx.StaticText(self)
  2356.         self.status_message.SetForegroundColour(wx.RED)
  2357.         self.apply.Enable(False)
  2358.         self.cancel = wx.Button(self, label = '')
  2359.         self.apply_sizer = wx.BoxSizer(wx.HORIZONTAL)
  2360.         self.apply_sizer.Add(self.status_message, 1, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
  2361.         self.apply_sizer.Add(self.apply, 0, wx.ALL, 5)
  2362.         self.apply_sizer.Add(self.cancel, 0, wx.ALL, 5)
  2363.         sizer.Add(self.apply_sizer, 0, wx.EXPAND)
  2364.         self.Bind(wx.EVT_CLOSE, self.Hide)
  2365.         self.Bind(wx.EVT_ICONIZE, self.Hide)
  2366.         self.Bind(wx.EVT_CLOSE, self.Hide)
  2367.         self.cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  2368.         self.apply.Bind(wx.EVT_BUTTON, self.OnApply)
  2369.         self.tray = tray
  2370.         self.restart_required = False
  2371.         self.model = model
  2372.         self.lang = None
  2373.         self.UpdateText()
  2374.         model.AddView(self)
  2375.         width = []([ size for unused, size in self.intercepted.columns ])
  2376.         (unused, height) = self.GetBestFittingSize()
  2377.         self.SetSize((width, height))
  2378.         self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO))
  2379.  
  2380.     
  2381.     def OnCancel(self, evt):
  2382.         self.Hide()
  2383.  
  2384.     
  2385.     def OnApply(self, evt):
  2386.         options[('Storage', 'cache_expiry_days')] = int(self.setting.cache_expiry_days.GetValue())
  2387.         options.update_file(optionsPathname)
  2388.         old_intercepted = self.model.settings.mailapps
  2389.         self.model.SetSettings(self.setting.block.GetValue(), self.setting.modSubj.GetValue(), self.setting.tag.GetValue(), self.setting.lang.GetSelection(), self.intercepted.GetValue(), self.setting.auto_bugreport.GetValue(), self.setting.user_address.GetValue(), self.setting.date_header.GetSelection(), self.setting.auto_update.GetValue(), self.setting.on_startup.GetValue(), self.setting.vertical.GetValue(), self.periodic.GetValue(), self.setting.enable_period.GetValue(), self.setting.period.GetValue(), self.setting.notify_sound.GetValue(), self.setting.enable_balloons.GetValue())
  2390.         self.apply.Enable(False)
  2391.         if sorted(self.model.settings.mailapps) != sorted(old_intercepted):
  2392.             self.restart_required = True
  2393.             self.UpdateText()
  2394.         
  2395.  
  2396.     
  2397.     def UpdateView(self, model):
  2398.         if self.lang != model.settings.lang:
  2399.             self.tray.SetupLanguage(model.settings.lang)
  2400.             self.UpdateText()
  2401.             self.lang = model.settings.lang
  2402.         
  2403.  
  2404.     
  2405.     def Hide(self, event = None):
  2406.         wx.Dialog.Hide(self)
  2407.  
  2408.     
  2409.     def Show(self, hide = False):
  2410.         if hide:
  2411.             return self.Hide()
  2412.         
  2413.         self.model.UpdateViews(True)
  2414.         self.Raise()
  2415.         wx.Dialog.Show(self)
  2416.  
  2417.     
  2418.     def UpdateText(self):
  2419.         self.nb.SetPageText(0, _('General Settings'))
  2420.         self.nb.SetPageText(1, _('Intercepted Email Applications'))
  2421.         self.nb.SetPageText(2, _('Periodic Retrieval'))
  2422.         self.apply.SetTitle(_('Apply'))
  2423.         self.apply.SetBestFittingSize()
  2424.         self.cancel.SetTitle(_('Close'))
  2425.         self.cancel.SetBestFittingSize()
  2426.         if self.restart_required:
  2427.             label = textwrap.fill(_('In order for these changes to take effect, you must restart your computer.'), 40)
  2428.             self.status_message.SetLabel(label)
  2429.         else:
  2430.             self.status_message.SetLabel('')
  2431.         self.status_message.SetBestFittingSize()
  2432.         self.apply_sizer.Layout()
  2433.         self.GetSizer().Layout()
  2434.         self.setting.UpdateText()
  2435.         self.intercepted.UpdateText()
  2436.         self.periodic.UpdateText()
  2437.  
  2438.     
  2439.     def OnConfigure(self, event = None, page = 0):
  2440.         self.Show()
  2441.         self.Restore()
  2442.         self.nb.SetSelection(page)
  2443.  
  2444.  
  2445.  
  2446. class StatusDialog(wx.Dialog):
  2447.     
  2448.     def __init__(self, model, tray):
  2449.         title = _('Status and Statistics')
  2450.         pre = wx.PreDialog()
  2451.         pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
  2452.         pre.Create(None, -1, title = title)
  2453.         self.PostCreate(pre)
  2454.         self.SetBackgroundColour('white')
  2455.         sizer = wx.BoxSizer(wx.VERTICAL)
  2456.         self.SetSizer(sizer)
  2457.         self.status = Status(self, model)
  2458.         sizer.Add(self.status, 0, wx.EXPAND | wx.ALL, 10)
  2459.         self.tray = tray
  2460.         self.model = model
  2461.         self.lang = None
  2462.         self.UpdateText()
  2463.         model.AddView(self)
  2464.         self.Bind(wx.EVT_CLOSE, self.Hide)
  2465.         self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO))
  2466.  
  2467.     
  2468.     def UpdateView(self, model):
  2469.         if self.lang != model.settings.lang:
  2470.             self.tray.SetupLanguage(model.settings.lang)
  2471.             self.UpdateText()
  2472.             self.lang = model.settings.lang
  2473.         
  2474.  
  2475.     
  2476.     def Hide(self, event = None):
  2477.         wx.Dialog.Hide(self)
  2478.  
  2479.     
  2480.     def Show(self, hide = False):
  2481.         if hide:
  2482.             return self.Hide()
  2483.         
  2484.         self.model.UpdateViews(True)
  2485.         self.UpdateText()
  2486.         self.Raise()
  2487.         wx.Dialog.Show(self)
  2488.  
  2489.     
  2490.     def UpdateText(self):
  2491.         self.status.UpdateText()
  2492.         widths = [ c.GetSize()[0] for c in self.status.controls ]
  2493.         self.SetSize((w + 60, h))
  2494.  
  2495.     
  2496.     def OnStatus(self, event = None):
  2497.         self.Show()
  2498.         self.Restore()
  2499.  
  2500.  
  2501.  
  2502. class AboutDialog(wx.Dialog):
  2503.     
  2504.     def __init__(self, model, tray):
  2505.         if license.get_code():
  2506.             title = _('About SpamExperts Professional')
  2507.         else:
  2508.             title = _('About SpamExperts Home')
  2509.         pre = wx.PreDialog()
  2510.         pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
  2511.         pre.Create(None, -1, title = title)
  2512.         self.PostCreate(pre)
  2513.         self.SetBackgroundColour('white')
  2514.         sizer = wx.BoxSizer(wx.VERTICAL)
  2515.         self.SetSizer(sizer)
  2516.         self.about = AboutSE(self, model)
  2517.         sizer.Add(self.about, 0, wx.EXPAND | wx.ALL, 10)
  2518.         self.tray = tray
  2519.         self.model = model
  2520.         self.lang = None
  2521.         self.UpdateText()
  2522.         model.AddView(self)
  2523.         self.Bind(wx.EVT_CLOSE, self.Hide)
  2524.         self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO))
  2525.  
  2526.     
  2527.     def UpdateView(self, model):
  2528.         if self.lang != model.settings.lang:
  2529.             self.tray.SetupLanguage(model.settings.lang)
  2530.             self.UpdateText()
  2531.             self.lang = model.settings.lang
  2532.         
  2533.  
  2534.     
  2535.     def Hide(self, event = None):
  2536.         wx.Dialog.Hide(self)
  2537.  
  2538.     
  2539.     def Show(self, hide = False):
  2540.         if hide:
  2541.             return self.Hide()
  2542.         
  2543.         self.model.UpdateViews(True)
  2544.         self.Raise()
  2545.         wx.Dialog.Show(self)
  2546.  
  2547.     
  2548.     def UpdateText(self):
  2549.         self.about.UpdateText()
  2550.         self.SetBestFittingSize()
  2551.  
  2552.     
  2553.     def OnAbout(self, event = None):
  2554.         self.Show()
  2555.         self.Restore()
  2556.  
  2557.  
  2558.  
  2559. class SEApp(wx.App):
  2560.     
  2561.     def __init__(self, model, tray, *args, **kwargs):
  2562.         self.model = model
  2563.         self.tray = tray
  2564.         wx.App.__init__(self, *args, **kwargs)
  2565.  
  2566.     
  2567.     def OnInit(self):
  2568.         self.window = MainFrame(self.model, self.tray)
  2569.         self.settings_window = SettingsDialog(self.model, self.tray)
  2570.         self.about_window = AboutDialog(self.model, self.tray)
  2571.         self.status_window = StatusDialog(self.model, self.tray)
  2572.         self.SetTopWindow(self.window)
  2573.         provider = wx.SimpleHelpProvider()
  2574.         wx.HelpProvider_Set(provider)
  2575.         return True
  2576.  
  2577.  
  2578. app = None
  2579.  
  2580. def main(model, tray):
  2581.     global app
  2582.     
  2583.     try:
  2584.         app = SEApp(model, tray, 0, useBestVisual = True)
  2585.         if options[('globals', 'verbose')]:
  2586.             print 'Entering main GUI loop.'
  2587.         
  2588.         app.MainLoop()
  2589.         if options[('globals', 'verbose')]:
  2590.             print 'Main GUI loop is finished.'
  2591.     except Exception:
  2592.         e = None
  2593.         tray.PostError(str(e))
  2594.         raise 
  2595.         return None
  2596.  
  2597.     if options[('globals', 'verbose')]:
  2598.         print 'Exiting GUI.'
  2599.     
  2600.  
  2601.